From 102d0b7b6970523ca7040e30d4e4fd1a349a01cc Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 26 Nov 2015 18:51:09 +0900 Subject: [PATCH] TIF: add signal detection feature for HW inputs Bug: 23820259 Change-Id: If7f03b49f7510ec405dfc4fb2b6f709b8c8b820d --- api/system-current.txt | 3 + media/java/android/media/tv/TvInputManager.java | 8 +-- media/java/android/media/tv/TvStreamConfig.java | 29 +++++++- .../android/server/tv/TvInputHardwareManager.java | 38 ++++++---- .../core/jni/com_android_server_tv_TvInputHal.cpp | 81 ++++++++++++++++------ 5 files changed, 119 insertions(+), 40 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index 40347cbfa1ac..53bfc980b2a6 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -23774,6 +23774,7 @@ package android.media.tv { public class TvStreamConfig implements android.os.Parcelable { method public int describeContents(); + method public int getFlags(); method public int getGeneration(); method public int getMaxHeight(); method public int getMaxWidth(); @@ -23781,6 +23782,7 @@ package android.media.tv { method public int getType(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_MASK_SIGNAL_DETECTION = 1; // 0x1 field public static final int STREAM_TYPE_BUFFER_PRODUCER = 2; // 0x2 field public static final int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1; // 0x1 } @@ -23788,6 +23790,7 @@ package android.media.tv { public static final class TvStreamConfig.Builder { ctor public TvStreamConfig.Builder(); method public android.media.tv.TvStreamConfig build(); + method public android.media.tv.TvStreamConfig.Builder flags(int); method public android.media.tv.TvStreamConfig.Builder generation(int); method public android.media.tv.TvStreamConfig.Builder maxHeight(int); method public android.media.tv.TvStreamConfig.Builder maxWidth(int); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index e9c94c07dde4..6a13f826949a 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -129,9 +129,8 @@ public final class TvInputManager { * The TV input is connected. * *

This state indicates that a source device is connected to the input port and is in the - * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input. This is - * the default state for any hardware inputs where their states are unknown. Non-hardware inputs - * are considered connected all the time. + * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input. + * Non-hardware inputs are considered connected all the time. * * @see #getInputState * @see TvInputManager.TvInputCallback#onInputStateChanged @@ -141,7 +140,8 @@ public final class TvInputManager { * The TV input is connected but in standby mode. * *

This state indicates that a source device is connected to the input port but is in standby - * mode. It is mostly relevant to hardware inputs such as HDMI input. + * or low power mode. It is mostly relevant to hardware inputs such as HDMI inputs and Component + * inputs. * * @see #getInputState * @see TvInputManager.TvInputCallback#onInputStateChanged diff --git a/media/java/android/media/tv/TvStreamConfig.java b/media/java/android/media/tv/TvStreamConfig.java index 0c2f3fec26a4..eae83cfff5f1 100644 --- a/media/java/android/media/tv/TvStreamConfig.java +++ b/media/java/android/media/tv/TvStreamConfig.java @@ -28,8 +28,15 @@ import android.util.Log; public class TvStreamConfig implements Parcelable { static final String TAG = TvStreamConfig.class.getSimpleName(); + // Must be in sync with tv_input.h public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1; public final static int STREAM_TYPE_BUFFER_PRODUCER = 2; + /** + * A flag indicating whether the HAL is sure about signal at this stream. Note that + * value of 0 here does not necessarily mean no signal. It just means that it may not have + * signal and the underlying layer is not sure. + */ + public static final int FLAG_MASK_SIGNAL_DETECTION = 0x1; private int mStreamId; private int mType; @@ -41,6 +48,10 @@ public class TvStreamConfig implements Parcelable { * via tv_input_device::get_stream_configurations(). */ private int mGeneration; + /** + * Flags for stream status. See FLAG_MASK_* for details. + */ + private int mFlags; public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -52,7 +63,8 @@ public class TvStreamConfig implements Parcelable { type(source.readInt()). maxWidth(source.readInt()). maxHeight(source.readInt()). - generation(source.readInt()).build(); + generation(source.readInt()). + flags(source.readInt()).build(); } catch (Exception e) { Log.e(TAG, "Exception creating TvStreamConfig from parcel", e); return null; @@ -87,6 +99,10 @@ public class TvStreamConfig implements Parcelable { return mGeneration; } + public int getFlags() { + return mFlags; + } + @Override public String toString() { return "TvStreamConfig {mStreamId=" + mStreamId + ";" + "mType=" + mType + ";mGeneration=" @@ -106,6 +122,7 @@ public class TvStreamConfig implements Parcelable { dest.writeInt(mMaxWidth); dest.writeInt(mMaxHeight); dest.writeInt(mGeneration); + dest.writeInt(mFlags); } /** @@ -117,6 +134,7 @@ public class TvStreamConfig implements Parcelable { private Integer mMaxWidth; private Integer mMaxHeight; private Integer mGeneration; + private int mFlags = 0; public Builder() { } @@ -146,6 +164,11 @@ public class TvStreamConfig implements Parcelable { return this; } + public Builder flags(int flag) { + mFlags = flag; + return this; + } + public TvStreamConfig build() { if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null || mGeneration == null) { @@ -158,6 +181,7 @@ public class TvStreamConfig implements Parcelable { config.mMaxWidth = mMaxWidth; config.mMaxHeight = mMaxHeight; config.mGeneration = mGeneration; + config.mFlags = mFlags; return config; } } @@ -172,6 +196,7 @@ public class TvStreamConfig implements Parcelable { && config.mStreamId == mStreamId && config.mType == mType && config.mMaxWidth == mMaxWidth - && config.mMaxHeight == mMaxHeight; + && config.mMaxHeight == mMaxHeight + && config.mFlags == mFlags; } } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 7f4c42b5c686..9bf7ae4465ad 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -17,6 +17,7 @@ package com.android.server.tv; import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; +import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY; import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED; import android.content.BroadcastReceiver; @@ -102,7 +103,6 @@ class TvInputHardwareManager implements TvInputHal.Callback { private int mCurrentIndex = 0; private int mCurrentMaxIndex = 0; - // TODO: Should handle STANDBY case. private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray(); private final List mPendingHdmiDeviceEvents = new LinkedList<>(); @@ -206,7 +206,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { String inputId = mHardwareInputIdMap.get(deviceId); if (inputId != null) { mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget(); + obtainStateFromConfigs(configs), 0, inputId).sendToTarget(); } ITvInputHardwareCallback callback = connection.getCallbackLocked(); if (callback != null) { @@ -256,12 +256,13 @@ class TvInputHardwareManager implements TvInputHal.Callback { || connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId; } - private int convertConnectedToState(boolean connected) { - if (connected) { - return INPUT_STATE_CONNECTED; - } else { - return INPUT_STATE_DISCONNECTED; + private int obtainStateFromConfigs(TvStreamConfig[] configs) { + for (TvStreamConfig config : configs) { + if ((config.getFlags() & TvStreamConfig.FLAG_MASK_SIGNAL_DETECTION) != 0) { + return INPUT_STATE_CONNECTED; + } } + return (configs.length > 0) ? INPUT_STATE_CONNECTED_STANDBY : INPUT_STATE_DISCONNECTED; } public void addHardwareTvInput(int deviceId, TvInputInfo info) { @@ -286,9 +287,14 @@ class TvInputHardwareManager implements TvInputHal.Callback { } String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId()); if (inputId != null && inputId.equals(info.getId())) { - mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - convertConnectedToState(mHdmiStateMap.valueAt(i)), 0, - inputId).sendToTarget(); + // No HDMI hotplug does not necessarily mean disconnected, as old devices may + // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to + // denote unknown state. + int state = mHdmiStateMap.valueAt(i) + ? INPUT_STATE_CONNECTED + : INPUT_STATE_CONNECTED_STANDBY; + mHandler.obtainMessage( + ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget(); return; } } @@ -296,7 +302,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { Connection connection = mConnections.get(deviceId); if (connection != null) { mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - convertConnectedToState(connection.getConfigsLocked().length > 0), 0, + obtainStateFromConfigs(connection.getConfigsLocked()), 0, info.getId()).sendToTarget(); } } @@ -1110,8 +1116,14 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (inputId == null) { return; } - mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget(); + // No HDMI hotplug does not necessarily mean disconnected, as old devices may + // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to + // denote unknown state. + int state = event.isConnected() + ? INPUT_STATE_CONNECTED + : INPUT_STATE_CONNECTED_STANDBY; + mHandler.obtainMessage( + ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget(); } } } diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index 01acdeffb853..6c640ba55042 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -54,6 +54,7 @@ static struct { jmethodID maxWidth; jmethodID maxHeight; jmethodID generation; + jmethodID flags; jmethodID build; } gTvStreamConfigBuilderClassInfo; @@ -239,7 +240,7 @@ public: int addOrUpdateStream(int deviceId, int streamId, const sp& surface); int removeStream(int deviceId, int streamId); - const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs); + const tv_stream_config_ext_t* getStreamConfigs(int deviceId, int* numConfigs); void onDeviceAvailable(const tv_input_device_info_t& info); void onDeviceUnavailable(int deviceId); @@ -288,10 +289,15 @@ private: sp mLooper; KeyedVector > mConnections; + + tv_stream_config_ext_t* mConfigBuffer; + int mConfigBufferSize; }; JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device, - const sp& looper) { + const sp& looper) + : mConfigBuffer(NULL), + mConfigBufferSize(0) { mThiz = env->NewWeakGlobalRef(thiz); mDevice = device; mCallback.notify = &JTvInputHal::notify; @@ -306,6 +312,10 @@ JTvInputHal::~JTvInputHal() { JNIEnv* env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mThiz); mThiz = NULL; + + if (mConfigBuffer != NULL) { + delete[] mConfigBuffer; + } } JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp& looper) { @@ -354,15 +364,14 @@ int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp if (connection.mSourceHandle == NULL && connection.mThread == 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) { + const tv_stream_config_ext_t* configs = getStreamConfigs(deviceId, &numConfigs); + if (configs == NULL) { 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) { + if (configs[i].config.stream_id == streamId) { configIndex = i; break; } @@ -371,13 +380,13 @@ int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp ALOGE("Cannot find a config with given stream ID: %d", streamId); return BAD_VALUE; } - connection.mStreamType = configs[configIndex].type; + connection.mStreamType = configs[configIndex].config.type; tv_stream_t stream; - stream.stream_id = configs[configIndex].stream_id; + stream.stream_id = configs[configIndex].config.stream_id; if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) { - stream.buffer_producer.width = configs[configIndex].max_video_width; - stream.buffer_producer.height = configs[configIndex].max_video_height; + stream.buffer_producer.width = configs[configIndex].config.max_video_width; + stream.buffer_producer.height = configs[configIndex].config.max_video_height; } if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) { ALOGE("Couldn't add stream"); @@ -431,12 +440,33 @@ int JTvInputHal::removeStream(int deviceId, int streamId) { 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; +const tv_stream_config_ext_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) { + const tv_stream_config_ext_t* configs = NULL; + if (mDevice->common.version >= TV_INPUT_DEVICE_API_VERSION_0_2) { + if (mDevice->get_stream_configurations_ext( + mDevice, deviceId, numConfigs, &configs) != 0) { + ALOGE("Couldn't get stream configs"); + return NULL; + } + } else { + const tv_stream_config_t* oldConfigs; + if (mDevice->get_stream_configurations( + mDevice, deviceId, numConfigs, &oldConfigs) != 0) { + ALOGE("Couldn't get stream configs"); + return NULL; + } + if (mConfigBufferSize < *numConfigs) { + mConfigBufferSize = (*numConfigs / 16 + 1) * 16; + if (mConfigBuffer != NULL) { + delete[] mConfigBuffer; + } + mConfigBuffer = new tv_stream_config_ext_t[mConfigBufferSize]; + } + for (int i = 0; i < *numConfigs; ++i) { + mConfigBuffer[i].config = oldConfigs[i]; + mConfigBuffer[i].flags = 0; + } + configs = mConfigBuffer; } return configs; } @@ -629,7 +659,7 @@ 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); + const tv_stream_config_ext_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs); jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL); for (int i = 0; i < numConfigs; ++i) { @@ -637,15 +667,20 @@ static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz, gTvStreamConfigBuilderClassInfo.clazz, gTvStreamConfigBuilderClassInfo.constructor); env->CallObjectMethod( - builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id); + builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].config.stream_id); env->CallObjectMethod( - builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type); + builder, gTvStreamConfigBuilderClassInfo.type, configs[i].config.type); env->CallObjectMethod( - builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width); + builder, gTvStreamConfigBuilderClassInfo.maxWidth, + configs[i].config.max_video_width); env->CallObjectMethod( - builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height); + builder, gTvStreamConfigBuilderClassInfo.maxHeight, + configs[i].config.max_video_height); env->CallObjectMethod( builder, gTvStreamConfigBuilderClassInfo.generation, generation); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.flags, + configs[i].flags); jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build); @@ -737,6 +772,10 @@ int register_android_server_tv_TvInputHal(JNIEnv* env) { gTvStreamConfigBuilderClassInfo.clazz, "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.flags, + gTvStreamConfigBuilderClassInfo.clazz, + "flags", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( gTvStreamConfigBuilderClassInfo.build, gTvStreamConfigBuilderClassInfo.clazz, "build", "()Landroid/media/tv/TvStreamConfig;"); -- 2.11.0