diff options
Diffstat (limited to 'services')
7 files changed, 895 insertions, 2 deletions
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index c1a4643..9133ce9 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1463,7 +1463,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. - mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; + mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving); // If the caller has asked not to resume at this point, we make note @@ -1473,7 +1473,8 @@ public final class ActivityStackSupervisor implements DisplayListener { r.delayedResume = true; } - ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; + ActivityRecord notTop = + (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; // If the onlyIfNeeded flag is set, then we can do this if the activity // being launched is the same as the one making the call... or, as @@ -1496,9 +1497,11 @@ public final class ActivityStackSupervisor implements DisplayListener { case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS: intent.addFlags( Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + launchFlags = intent.getFlags(); break; case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING: intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + launchFlags = intent.getFlags(); break; } final boolean newDocument = intent.isDocument(); diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java new file mode 100644 index 0000000..4bdd2be --- /dev/null +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -0,0 +1,128 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.tv; + +import android.os.Handler; +import android.os.HandlerThread; +import android.tv.TvInputHardwareInfo; +import android.tv.TvStreamConfig; +import android.view.Surface; + +/** + * Provides access to the low-level TV input hardware abstraction layer. + */ +final class TvInputHal { + public final static int SUCCESS = 0; + public final static int ERROR_NO_INIT = -1; + public final static int ERROR_STALE_CONFIG = -2; + public final static int ERROR_UNKNOWN = -3; + + public static final int TYPE_HDMI = 1; + public static final int TYPE_BUILT_IN_TUNER = 2; + public static final int TYPE_PASSTHROUGH = 3; + + public interface Callback { + public void onDeviceAvailable( + TvInputHardwareInfo info, TvStreamConfig[] configs); + public void onDeviceUnavailable(int deviceId); + public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs); + } + + private native long nativeOpen(); + + private static native int nativeSetSurface(long ptr, int deviceId, int streamId, + Surface surface); + private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId, + int generation); + private static native void nativeClose(long ptr); + + private long mPtr = 0l; + private final Callback mCallback; + private final HandlerThread mThread = new HandlerThread("TV input HAL event thread"); + private final Handler mHandler; + private int mStreamConfigGeneration = 0; + private TvStreamConfig[] mStreamConfigs; + + public TvInputHal(Callback callback) { + mCallback = callback; + mThread.start(); + mHandler = new Handler(mThread.getLooper()); + } + + public void init() { + mPtr = nativeOpen(); + } + + public int setSurface(int deviceId, Surface surface, TvStreamConfig streamConfig) { + if (mPtr == 0) { + return ERROR_NO_INIT; + } + if (mStreamConfigGeneration != streamConfig.getGeneration()) { + return ERROR_STALE_CONFIG; + } + if (nativeSetSurface(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) { + return SUCCESS; + } else { + return ERROR_UNKNOWN; + } + } + + public void close() { + if (mPtr != 0l) { + nativeClose(mPtr); + mThread.quitSafely(); + } + } + + private synchronized void retrieveStreamConfigs(int deviceId) { + ++mStreamConfigGeneration; + mStreamConfigs = nativeGetStreamConfigs(mPtr, deviceId, mStreamConfigGeneration); + } + + // Called from native + private void deviceAvailableFromNative(int deviceId, int type) { + final TvInputHardwareInfo info = new TvInputHardwareInfo(deviceId, type); + mHandler.post(new Runnable() { + @Override + public void run() { + retrieveStreamConfigs(info.getDeviceId()); + mCallback.onDeviceAvailable(info, mStreamConfigs); + } + }); + } + + private void deviceUnavailableFromNative(int deviceId) { + final int id = deviceId; + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onDeviceUnavailable(id); + } + }); + } + + private void streamConfigsChangedFromNative(int deviceId) { + final int id = deviceId; + mHandler.post(new Runnable() { + @Override + public void run() { + retrieveStreamConfigs(id); + mCallback.onStreamConfigurationChanged(id, mStreamConfigs); + } + }); + } +} diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java new file mode 100644 index 0000000..b95b0f0 --- /dev/null +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.tv; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.tv.ITvInputHardware; +import android.tv.ITvInputHardwareCallback; +import android.tv.TvInputHardwareInfo; +import android.tv.TvStreamConfig; +import android.util.Slog; +import android.util.SparseArray; +import android.view.KeyEvent; +import android.view.Surface; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A helper class for TvInputManagerService to handle TV input hardware. + * + * This class does a basic connection management and forwarding calls to TvInputHal which eventually + * calls to tv_input HAL module. + * + * @hide + */ +class TvInputHardwareManager implements TvInputHal.Callback { + private static final String TAG = TvInputHardwareManager.class.getSimpleName(); + private final TvInputHal mHal = new TvInputHal(this); + private final SparseArray<Connection> mConnections = new SparseArray<Connection>(); + private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>(); + private final Context mContext; + private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>(); + + private final Object mLock = new Object(); + + public TvInputHardwareManager(Context context) { + mContext = context; + // TODO(hdmi): mHdmiManager = mContext.getSystemService(...); + // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient(); + mHal.init(); + } + + @Override + public void onDeviceAvailable( + TvInputHardwareInfo info, TvStreamConfig[] configs) { + synchronized (mLock) { + Connection connection = new Connection(info); + connection.updateConfigsLocked(configs); + mConnections.put(info.getDeviceId(), connection); + buildInfoListLocked(); + // TODO: notify if necessary + } + } + + private void buildInfoListLocked() { + mInfoList.clear(); + for (int i = 0; i < mConnections.size(); ++i) { + mInfoList.add(mConnections.valueAt(i).getInfoLocked()); + } + } + + @Override + public void onDeviceUnavailable(int deviceId) { + synchronized (mLock) { + Connection connection = mConnections.get(deviceId); + if (connection == null) { + Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId); + return; + } + connection.resetLocked(null, null, null, null); + mConnections.remove(deviceId); + buildInfoListLocked(); + // TODO: notify if necessary + } + } + + @Override + public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) { + synchronized (mLock) { + Connection connection = mConnections.get(deviceId); + if (connection == null) { + Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with " + + deviceId); + return; + } + connection.updateConfigsLocked(configs); + try { + connection.getCallbackLocked().onStreamConfigChanged(configs); + } catch (RemoteException e) { + Slog.e(TAG, "onStreamConfigurationChanged: " + e); + } + } + } + + public List<TvInputHardwareInfo> getHardwareList() { + synchronized (mLock) { + return mInfoList; + } + } + + /** + * Create a TvInputHardware object with a specific deviceId. One service at a time can access + * the object, and if more than one process attempts to create hardware with the same deviceId, + * the latest service will get the object and all the other hardware are released. The + * release is notified via ITvInputHardwareCallback.onReleased(). + */ + public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback, + int callingUid, int resolvedUserId) { + if (callback == null) { + throw new NullPointerException(); + } + synchronized (mLock) { + Connection connection = mConnections.get(deviceId); + if (connection == null) { + Slog.e(TAG, "Invalid deviceId : " + deviceId); + return null; + } + if (connection.getCallingUidLocked() != callingUid + || connection.getResolvedUserIdLocked() != resolvedUserId) { + TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked()); + try { + callback.asBinder().linkToDeath(connection, 0); + } catch (RemoteException e) { + hardware.release(); + return null; + } + connection.resetLocked(hardware, callback, callingUid, resolvedUserId); + } + return connection.getHardwareLocked(); + } + } + + /** + * Release the specified hardware. + */ + public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid, + int resolvedUserId) { + synchronized (mLock) { + Connection connection = mConnections.get(deviceId); + if (connection == null) { + Slog.e(TAG, "Invalid deviceId : " + deviceId); + return; + } + if (connection.getHardwareLocked() != hardware + || connection.getCallingUidLocked() != callingUid + || connection.getResolvedUserIdLocked() != resolvedUserId) { + return; + } + connection.resetLocked(null, null, null, null); + } + } + + private class Connection implements IBinder.DeathRecipient { + private final TvInputHardwareInfo mInfo; + private TvInputHardwareImpl mHardware = null; + private ITvInputHardwareCallback mCallback; + private TvStreamConfig[] mConfigs = null; + private Integer mCallingUid = null; + private Integer mResolvedUserId = null; + + public Connection(TvInputHardwareInfo info) { + mInfo = info; + } + + // *Locked methods assume TvInputHardwareManager.mLock is held. + + public void resetLocked(TvInputHardwareImpl hardware, + ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) { + if (mHardware != null) { + try { + mCallback.onReleased(); + } catch (RemoteException e) { + Slog.e(TAG, "Connection::resetHardware: " + e); + } + mHardware.release(); + } + mHardware = hardware; + mCallback = callback; + mCallingUid = callingUid; + mResolvedUserId = resolvedUserId; + + if (mHardware != null && mCallback != null) { + try { + mCallback.onStreamConfigChanged(getConfigsLocked()); + } catch (RemoteException e) { + Slog.e(TAG, "Connection::resetHardware: " + e); + } + } + } + + public void updateConfigsLocked(TvStreamConfig[] configs) { + mConfigs = configs; + } + + public TvInputHardwareInfo getInfoLocked() { + return mInfo; + } + + public ITvInputHardware getHardwareLocked() { + return mHardware; + } + + public ITvInputHardwareCallback getCallbackLocked() { + return mCallback; + } + + public TvStreamConfig[] getConfigsLocked() { + return mConfigs; + } + + public int getCallingUidLocked() { + return mCallingUid; + } + + public int getResolvedUserIdLocked() { + return mResolvedUserId; + } + + @Override + public void binderDied() { + synchronized (mLock) { + resetLocked(null, null, null, null); + } + } + } + + private class TvInputHardwareImpl extends ITvInputHardware.Stub { + private final TvInputHardwareInfo mInfo; + private boolean mReleased = false; + private final Object mImplLock = new Object(); + + public TvInputHardwareImpl(TvInputHardwareInfo info) { + mInfo = info; + } + + public void release() { + synchronized (mImplLock) { + mReleased = true; + } + } + + @Override + public boolean setSurface(Surface surface, TvStreamConfig config) + throws RemoteException { + synchronized (mImplLock) { + if (mReleased) { + throw new IllegalStateException("Device already released."); + } + if (mInfo.getType() == TvInputHal.TYPE_HDMI) { + if (surface != null) { + // Set "Active Source" for HDMI. + // TODO(hdmi): mHdmiClient.deviceSelect(...); + mActiveHdmiSources.add(mInfo.getDeviceId()); + } else { + mActiveHdmiSources.remove(mInfo.getDeviceId()); + if (mActiveHdmiSources.size() == 0) { + // Tell HDMI that no HDMI source is active + // TODO(hdmi): mHdmiClient.portSelect(null); + } + } + } + return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS; + } + } + + @Override + public void setVolume(float volume) throws RemoteException { + synchronized (mImplLock) { + if (mReleased) { + throw new IllegalStateException("Device already released."); + } + } + // TODO + } + + @Override + public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException { + synchronized (mImplLock) { + if (mReleased) { + throw new IllegalStateException("Device already released."); + } + } + if (mInfo.getType() != TvInputHal.TYPE_HDMI) { + return false; + } + // TODO(hdmi): mHdmiClient.sendKeyEvent(event); + return false; + } + } +} diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 8ad7fff..6c38a4c 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -42,11 +42,14 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.TvContract; import android.tv.ITvInputClient; +import android.tv.ITvInputHardware; +import android.tv.ITvInputHardwareCallback; import android.tv.ITvInputManager; import android.tv.ITvInputService; import android.tv.ITvInputServiceCallback; import android.tv.ITvInputSession; import android.tv.ITvInputSessionCallback; +import android.tv.TvInputHardwareInfo; import android.tv.TvInputInfo; import android.tv.TvInputService; import android.util.Slog; @@ -71,6 +74,7 @@ public final class TvInputManagerService extends SystemService { private static final String TAG = "TvInputManagerService"; private final Context mContext; + private final TvInputHardwareManager mTvInputHardwareManager; private final ContentResolver mContentResolver; @@ -92,6 +96,7 @@ public final class TvInputManagerService extends SystemService { mContentResolver = context.getContentResolver(); mLogHandler = new LogHandler(IoThread.get().getLooper()); + mTvInputHardwareManager = new TvInputHardwareManager(context); registerBroadcastReceivers(); synchronized (mLock) { @@ -730,6 +735,64 @@ public final class TvInputManagerService extends SystemService { Binder.restoreCallingIdentity(identity); } } + + @Override + public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { + if (mContext.checkCallingPermission( + android.Manifest.permission.TV_INPUT_HARDWARE) + != PackageManager.PERMISSION_GRANTED) { + return null; + } + + final long identity = Binder.clearCallingIdentity(); + try { + return mTvInputHardwareManager.getHardwareList(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public ITvInputHardware acquireTvInputHardware(int deviceId, + ITvInputHardwareCallback callback, int userId) throws RemoteException { + if (mContext.checkCallingPermission( + android.Manifest.permission.TV_INPUT_HARDWARE) + != PackageManager.PERMISSION_GRANTED) { + return null; + } + + final long identity = Binder.clearCallingIdentity(); + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "acquireTvInputHardware"); + try { + return mTvInputHardwareManager.acquireHardware( + deviceId, callback, callingUid, resolvedUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) + throws RemoteException { + if (mContext.checkCallingPermission( + android.Manifest.permission.TV_INPUT_HARDWARE) + != PackageManager.PERMISSION_GRANTED) { + return; + } + + final long identity = Binder.clearCallingIdentity(); + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "releaseTvInputHardware"); + try { + mTvInputHardwareManager.releaseHardware( + deviceId, hardware, callingUid, resolvedUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } } private static final class UserState { diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index 51583a5..3cfb45b 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -23,6 +23,7 @@ LOCAL_SRC_FILES += \ $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \ $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \ $(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \ + $(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \ $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \ $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \ $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \ diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp new file mode 100644 index 0000000..f0c4f3a --- /dev/null +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -0,0 +1,388 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "TvInputHal" + +//#define LOG_NDEBUG 0 + +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/android_view_Surface.h" +#include "JNIHelp.h" +#include "jni.h" + +#include <gui/Surface.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Log.h> +#include <utils/NativeHandle.h> +#include <hardware/tv_input.h> + +namespace android { + +static struct { + jmethodID deviceAvailable; + jmethodID deviceUnavailable; + jmethodID streamConfigsChanged; +} gTvInputHalClassInfo; + +static struct { + jclass clazz; +} gTvStreamConfigClassInfo; + +static struct { + jclass clazz; + + jmethodID constructor; + jmethodID streamId; + jmethodID type; + jmethodID maxWidth; + jmethodID maxHeight; + jmethodID generation; + jmethodID build; +} gTvStreamConfigBuilderClassInfo; + +//////////////////////////////////////////////////////////////////////////////// + +class JTvInputHal { +public: + ~JTvInputHal(); + + static JTvInputHal* createInstance(JNIEnv* env, jobject thiz); + + int setSurface(int deviceId, int streamId, const sp<Surface>& surface); + void getStreamConfigs(int deviceId, jobjectArray* array); + const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs); + +private: + class Connection { + public: + Connection() : mStreamId(0) {} + + sp<Surface> mSurface; + sp<NativeHandle> mSourceHandle; + int mStreamId; + }; + + JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev); + + static void notify( + tv_input_device_t* dev,tv_input_event_t* event, void* data); + + void onDeviceAvailable(const tv_input_device_info_t& info); + void onDeviceUnavailable(int deviceId); + void onStreamConfigurationsChanged(int deviceId); + + jweak mThiz; + tv_input_device_t* mDevice; + tv_input_callback_ops_t mCallback; + + KeyedVector<int, Connection> mConnections; +}; + +JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device) { + mThiz = env->NewWeakGlobalRef(thiz); + mDevice = device; + mCallback.notify = &JTvInputHal::notify; + + mDevice->initialize(mDevice, &mCallback, this); +} + +JTvInputHal::~JTvInputHal() { + mDevice->common.close((hw_device_t*)mDevice); + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteWeakGlobalRef(mThiz); + mThiz = NULL; +} + +JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) { + tv_input_module_t* module = NULL; + status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID, + (hw_module_t const**)&module); + if (err) { + ALOGE("Couldn't load %s module (%s)", + TV_INPUT_HARDWARE_MODULE_ID, strerror(-err)); + return 0; + } + + tv_input_device_t* device = NULL; + err = module->common.methods->open( + (hw_module_t*)module, + TV_INPUT_DEFAULT_DEVICE, + (hw_device_t**)&device); + if (err) { + ALOGE("Couldn't open %s device (%s)", + TV_INPUT_DEFAULT_DEVICE, strerror(-err)); + return 0; + } + + return new JTvInputHal(env, thiz, device); +} + +int JTvInputHal::setSurface(int deviceId, int streamId, const sp<Surface>& surface) { + Connection& connection = mConnections.editValueFor(deviceId); + if (connection.mStreamId == streamId && connection.mSurface == surface) { + // Nothing to do + return NO_ERROR; + } + if (Surface::isValid(connection.mSurface)) { + connection.mSurface.clear(); + } + if (surface == NULL) { + if (connection.mSurface != NULL) { + connection.mSurface->setSidebandStream(NULL); + connection.mSurface.clear(); + } + if (connection.mSourceHandle != NULL) { + // Need to reset streams + if (mDevice->close_stream( + mDevice, deviceId, connection.mStreamId) != 0) { + ALOGE("Couldn't remove stream"); + return BAD_VALUE; + } + connection.mSourceHandle.clear(); + } + return NO_ERROR; + } + connection.mSurface = surface; + if (connection.mSourceHandle == NULL) { + // Need to configure stream + int numConfigs = 0; + const tv_stream_config_t* configs = NULL; + if (mDevice->get_stream_configurations( + mDevice, deviceId, &numConfigs, &configs) != 0) { + ALOGE("Couldn't get stream configs"); + return UNKNOWN_ERROR; + } + int configIndex = -1; + for (int i = 0; i < numConfigs; ++i) { + if (configs[i].stream_id == streamId) { + configIndex = i; + break; + } + } + if (configIndex == -1) { + ALOGE("Cannot find a config with given stream ID: %d", streamId); + return BAD_VALUE; + } + // TODO: handle buffer producer profile. + if (configs[configIndex].type != + TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) { + ALOGE("Profiles other than independent video source is not yet " + "supported : type = %d", configs[configIndex].type); + return INVALID_OPERATION; + } + tv_stream_t stream; + stream.stream_id = configs[configIndex].stream_id; + if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) { + ALOGE("Couldn't add stream"); + return UNKNOWN_ERROR; + } + connection.mSourceHandle = NativeHandle::create( + stream.sideband_stream_source_handle, false); + connection.mStreamId = stream.stream_id; + connection.mSurface->setSidebandStream(connection.mSourceHandle); + } + return NO_ERROR; +} + +const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) { + const tv_stream_config_t* configs = NULL; + if (mDevice->get_stream_configurations( + mDevice, deviceId, numConfigs, &configs) != 0) { + ALOGE("Couldn't get stream configs"); + return NULL; + } + return configs; +} + + +// static +void JTvInputHal::notify( + tv_input_device_t* dev, tv_input_event_t* event, void* data) { + JTvInputHal* thiz = (JTvInputHal*)data; + switch (event->type) { + case TV_INPUT_EVENT_DEVICE_AVAILABLE: { + thiz->onDeviceAvailable(event->device_info); + } break; + case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: { + thiz->onDeviceUnavailable(event->device_info.device_id); + } break; + case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: { + thiz->onStreamConfigurationsChanged(event->device_info.device_id); + } break; + default: + ALOGE("Unrecognizable event"); + } +} + +void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + mConnections.add(info.device_id, Connection()); + env->CallVoidMethod( + mThiz, + gTvInputHalClassInfo.deviceAvailable, + info.device_id, + info.type); +} + +void JTvInputHal::onDeviceUnavailable(int deviceId) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + mConnections.removeItem(deviceId); + env->CallVoidMethod( + mThiz, + gTvInputHalClassInfo.deviceUnavailable, + deviceId); +} + +void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + mConnections.removeItem(deviceId); + env->CallVoidMethod( + mThiz, + gTvInputHalClassInfo.streamConfigsChanged, + deviceId); +} + +//////////////////////////////////////////////////////////////////////////////// + +static jlong nativeOpen(JNIEnv* env, jobject thiz) { + return (jlong)JTvInputHal::createInstance(env, thiz); +} + +static int nativeSetSurface(JNIEnv* env, jclass clazz, + jlong ptr, jint deviceId, jint streamId, jobject jsurface) { + JTvInputHal* tvInputHal = (JTvInputHal*)ptr; + sp<Surface> surface( + jsurface + ? android_view_Surface_getSurface(env, jsurface) + : NULL); + return tvInputHal->setSurface(deviceId, streamId, surface); +} + +static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz, + jlong ptr, jint deviceId, jint generation) { + JTvInputHal* tvInputHal = (JTvInputHal*)ptr; + int numConfigs = 0; + const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs); + + jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL); + for (int i = 0; i < numConfigs; ++i) { + jobject builder = env->NewObject( + gTvStreamConfigBuilderClassInfo.clazz, + gTvStreamConfigBuilderClassInfo.constructor); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.generation, generation); + + jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build); + + env->SetObjectArrayElement(result, i, config); + + env->DeleteLocalRef(config); + env->DeleteLocalRef(builder); + } + return result; +} + +static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) { + JTvInputHal* tvInputHal = (JTvInputHal*)ptr; + delete tvInputHal; +} + +static JNINativeMethod gTvInputHalMethods[] = { + /* name, signature, funcPtr */ + { "nativeOpen", "()J", + (void*) nativeOpen }, + { "nativeSetSurface", "(JIILandroid/view/Surface;)I", + (void*) nativeSetSurface }, + { "nativeGetStreamConfigs", "(JII)[Landroid/tv/TvStreamConfig;", + (void*) nativeGetStreamConfigs }, + { "nativeClose", "(J)V", + (void*) nativeClose }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className) + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName) + +int register_android_server_tv_TvInputHal(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal", + gTvInputHalMethods, NELEM(gTvInputHalMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + jclass clazz; + FIND_CLASS(clazz, "com/android/server/tv/TvInputHal"); + + GET_METHOD_ID( + gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V"); + GET_METHOD_ID( + gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V"); + GET_METHOD_ID( + gTvInputHalClassInfo.streamConfigsChanged, clazz, + "streamConfigsChangedFromNative", "(I)V"); + + FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/tv/TvStreamConfig"); + gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz)); + + FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/tv/TvStreamConfig$Builder"); + gTvStreamConfigBuilderClassInfo.clazz = + jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz)); + + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.constructor, + gTvStreamConfigBuilderClassInfo.clazz, + "<init>", "()V"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.streamId, + gTvStreamConfigBuilderClassInfo.clazz, + "streamId", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.type, + gTvStreamConfigBuilderClassInfo.clazz, + "type", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.maxWidth, + gTvStreamConfigBuilderClassInfo.clazz, + "maxWidth", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.maxHeight, + gTvStreamConfigBuilderClassInfo.clazz, + "maxHeight", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.generation, + gTvStreamConfigBuilderClassInfo.clazz, + "generation", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.build, + gTvStreamConfigBuilderClassInfo.clazz, + "build", "()Landroid/tv/TvStreamConfig;"); + + return 0; +} + +} /* namespace android */ diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 1feb325..bfa8286 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -41,6 +41,7 @@ int register_android_server_dreams_McuHal(JNIEnv* env); int register_android_server_hdmi_HdmiCecController(JNIEnv* env); int register_android_server_hdmi_HdmiCecService(JNIEnv* env); int register_android_server_hdmi_HdmiMhlController(JNIEnv* env); +int register_android_server_tv_TvInputHal(JNIEnv* env); }; using namespace android; @@ -78,6 +79,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) // TODO: remove this once replaces HdmiCecService with HdmiControlService. register_android_server_hdmi_HdmiCecService(env); register_android_server_hdmi_HdmiMhlController(env); + register_android_server_tv_TvInputHal(env); return JNI_VERSION_1_4; } |