Loading media/java/android/media/tv/ITvInputService.aidl +7 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.media.tv; import android.media.tv.ITvInputServiceCallback; import android.media.tv.ITvInputSessionCallback; import android.media.tv.TvInputHardwareInfo; import android.view.InputChannel; /** Loading @@ -27,5 +28,10 @@ import android.view.InputChannel; oneway interface ITvInputService { void registerCallback(ITvInputServiceCallback callback); void unregisterCallback(in ITvInputServiceCallback callback); void createSession(in InputChannel channel, ITvInputSessionCallback callback); void createSession(in InputChannel channel, ITvInputSessionCallback callback, in String inputId); // For hardware TvInputService void notifyHardwareAdded(in TvInputHardwareInfo info); void notifyHardwareRemoved(int deviceId); } media/java/android/media/tv/ITvInputServiceCallback.aidl +5 −4 Original line number Diff line number Diff line Loading @@ -16,13 +16,14 @@ package android.media.tv; import android.content.ComponentName; import android.media.tv.TvInputInfo; /** * Helper interface for ITvInputService to allow the TV input to notify the client when its status * has been changed. * Helper interface for ITvInputService to allow the service to notify the * TvInputManagerService. * @hide */ oneway interface ITvInputServiceCallback { void onInputStateChanged(int state); void addTvInput(in TvInputInfo inputInfo); void removeTvInput(in String inputId); } media/java/android/media/tv/TvInputInfo.java +71 −7 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.media.tv.TvInputHardwareInfo; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; Loading Loading @@ -88,9 +90,46 @@ public final class TvInputInfo implements Parcelable { * instantiating it from the given Context and ResolveInfo. * * @param service The ResolveInfo returned from the package manager about this TV input service. * @hide */ * @hide */ public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service) throws XmlPullParserException, IOException { return createTvInputInfo(context, service, generateInputIdForComponentName( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name))); } /** * Create a new instance of the TvInputInfo class, * instantiating it from the given Context, ResolveInfo, and HdmiCecDeviceInfo. * * @param service The ResolveInfo returned from the package manager about this TV input service. * @param cecInfo The HdmiCecDeviceInfo for a HDMI CEC logical device. * @hide */ public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, HdmiCecDeviceInfo cecInfo) throws XmlPullParserException, IOException { return createTvInputInfo(context, service, generateInputIdForHdmiCec( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), cecInfo)); } /** * Create a new instance of the TvInputInfo class, * instantiating it from the given Context, ResolveInfo, and TvInputHardwareInfo. * * @param service The ResolveInfo returned from the package manager about this TV input service. * @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device. * @hide */ public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, TvInputHardwareInfo hardwareInfo) throws XmlPullParserException, IOException { return createTvInputInfo(context, service, generateInputIdForHardware( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), hardwareInfo)); } private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, String id) throws XmlPullParserException, IOException { ServiceInfo si = service.serviceInfo; PackageManager pm = context.getPackageManager(); XmlResourceParser parser = null; Loading @@ -115,7 +154,7 @@ public final class TvInputInfo implements Parcelable { "Meta-data does not start with tv-input-service tag in " + si.name); } TvInputInfo input = new TvInputInfo(context, service, null); TvInputInfo input = new TvInputInfo(context, service, id, null); TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.TvInputService); input.mSetupActivity = sa.getString( Loading Loading @@ -153,12 +192,13 @@ public final class TvInputInfo implements Parcelable { * Constructor. * * @param service The ResolveInfo returned from the package manager about this TV input service. * @hide * @param id ID of this TV input. Should be generated via generateInputId*(). * @param parentId ID of this TV input's parent input. {@code null} if none exists. */ private TvInputInfo(Context context, ResolveInfo service, String parentId) { private TvInputInfo(Context context, ResolveInfo service, String id, String parentId) { mService = service; ServiceInfo si = service.serviceInfo; mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name)); mId = id; mParentId = parentId; } Loading Loading @@ -311,12 +351,36 @@ public final class TvInputInfo implements Parcelable { * * @param name the component name for generating an input id. * @return the generated input id for the given {@code name}. * @hide */ public static final String generateInputIdForComponentName(ComponentName name) { private static final String generateInputIdForComponentName(ComponentName name) { return name.flattenToShortString(); } /** * Used to generate an input id from a ComponentName and HdmiCecDeviceInfo. * * @param name the component name for generating an input id. * @param cecInfo HdmiCecDeviceInfo describing this TV input. * @return the generated input id for the given {@code name} and {@code cecInfo}. */ private static final String generateInputIdForHdmiCec( ComponentName name, HdmiCecDeviceInfo cecInfo) { return name.flattenToShortString() + String.format("|CEC%08X%08X", cecInfo.getPhysicalAddress(), cecInfo.getLogicalAddress()); } /** * Used to generate an input id from a ComponentName and TvInputHardwareInfo * * @param name the component name for generating an input id. * @param hardwareInfo TvInputHardwareInfo describing this TV input. * @return the generated input id for the given {@code name} and {@code hardwareInfo}. */ private static final String generateInputIdForHardware( ComponentName name, TvInputHardwareInfo hardwareInfo) { return name.flattenToShortString() + String.format("|HW%d", hardwareInfo.getDeviceId()); } /** * Used to make this class parcelable. * Loading media/java/android/media/tv/TvInputService.java +98 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.media.tv; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.Service; import android.content.ComponentName; import android.content.Context; Loading Loading @@ -104,7 +105,8 @@ public abstract class TvInputService extends Service { } @Override public void createSession(InputChannel channel, ITvInputSessionCallback cb) { public void createSession(InputChannel channel, ITvInputSessionCallback cb, String inputId) { if (channel == null) { Log.w(TAG, "Creating session without input channel"); } Loading @@ -114,8 +116,21 @@ public abstract class TvInputService extends Service { SomeArgs args = SomeArgs.obtain(); args.arg1 = channel; args.arg2 = cb; args.arg3 = inputId; mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget(); } @Override public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) { mHandler.obtainMessage(ServiceHandler.DO_ADD_TV_INPUT_FROM_HARDWARE, hardwareInfo).sendToTarget(); } @Override public void notifyHardwareRemoved(int deviceId) { mHandler.obtainMessage(ServiceHandler.DO_REMOVE_TV_INPUT_FROM_HARDWARE, deviceId, 0).sendToTarget(); } }; } Loading @@ -137,6 +152,44 @@ public abstract class TvInputService extends Service { */ public abstract Session onCreateSession(); /** * Returns a concrete implementation of {@link Session}. * <p> * May return {@code null} if this TV input service fails to create a session for some reason. * </p> * @param inputId The ID of the TV input associated with the session. * * @hide */ @SystemApi public Session onCreateSession(String inputId) { return onCreateSession(); } /** * Returns a new TvInputInfo object if this service is responsible for {@code hardwareInfo}; * otherwise, return {@code null}. Override to modify default behavior of ignoring all input. * * @param hardwareInfo TvInputHardwareInfo object just added. * * @hide */ @SystemApi public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) { return null; } /** * Returns the input ID for {@code deviceId} if it is handled by this service; * otherwise, return {@code null}. Override to modify default behavior of ignoring all input. * * @hide */ @SystemApi public String onHardwareRemoved(int deviceId) { return null; } /** * Base class for derived classes to implement to provide a TV input session. */ Loading Loading @@ -715,6 +768,32 @@ public abstract class TvInputService extends Service { @SuppressLint("HandlerLeak") private final class ServiceHandler extends Handler { private static final int DO_CREATE_SESSION = 1; private static final int DO_ADD_TV_INPUT_FROM_HARDWARE = 2; private static final int DO_REMOVE_TV_INPUT_FROM_HARDWARE = 3; private void broadcastAddTvInput(TvInputInfo inputInfo) { int n = mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { try { mCallbacks.getBroadcastItem(i).addTvInput(inputInfo); } catch (RemoteException e) { Log.e(TAG, "Error while broadcasting: " + e); } } mCallbacks.finishBroadcast(); } private void broadcastRemoveTvInput(String inputId) { int n = mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { try { mCallbacks.getBroadcastItem(i).removeTvInput(inputId); } catch (RemoteException e) { Log.e(TAG, "Error while broadcasting: " + e); } } mCallbacks.finishBroadcast(); } @Override public final void handleMessage(Message msg) { Loading @@ -723,8 +802,9 @@ public abstract class TvInputService extends Service { SomeArgs args = (SomeArgs) msg.obj; InputChannel channel = (InputChannel) args.arg1; ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2; String inputId = (String) args.arg3; try { Session sessionImpl = onCreateSession(); Session sessionImpl = onCreateSession(inputId); if (sessionImpl == null) { // Failed to create a session. cb.onSessionCreated(null); Loading @@ -740,6 +820,22 @@ public abstract class TvInputService extends Service { args.recycle(); return; } case DO_ADD_TV_INPUT_FROM_HARDWARE: { TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj; TvInputInfo inputInfo = onHardwareAdded(hardwareInfo); if (inputInfo != null) { broadcastAddTvInput(inputInfo); } return; } case DO_REMOVE_TV_INPUT_FROM_HARDWARE: { int deviceId = msg.arg1; String inputId = onHardwareRemoved(deviceId); if (inputId != null) { broadcastRemoveTvInput(inputId); } return; } default: { Log.w(TAG, "Unhandled message code: " + msg.what); return; Loading services/core/java/com/android/server/tv/TvInputHardwareManager.java +59 −21 Original line number Diff line number Diff line Loading @@ -20,8 +20,10 @@ import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED; import android.content.Context; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiHotplugEvent; import android.hardware.hdmi.IHdmiDeviceEventListener; import android.media.AudioDevicePort; import android.media.AudioManager; import android.media.AudioPatch; Loading @@ -33,7 +35,6 @@ import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvInputInfo; import android.media.tv.TvStreamConfig; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; Loading Loading @@ -66,27 +67,24 @@ class TvInputHardwareManager private final SparseArray<Connection> mConnections = new SparseArray<Connection>(); private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>(); private final Context mContext; private final TvInputManagerService.Client mClient; private final Listener mListener; private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>(); private final AudioManager mAudioManager; private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray(); // TODO: Should handle INACTIVE case. private final SparseArray<TvInputInfo> mTvInputInfoMap = new SparseArray<TvInputInfo>(); private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener(); // Calls to mClient should happen here. private final HandlerThread mHandlerThread = new HandlerThread(TAG); private final Handler mHandler; // Calls to mListener should happen here. private final Handler mHandler = new ListenerHandler(); private final Object mLock = new Object(); public TvInputHardwareManager(Context context, TvInputManagerService.Client client) { public TvInputHardwareManager(Context context, Listener listener) { mContext = context; mClient = client; mListener = listener; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mHal.init(); mHandlerThread.start(); mHandler = new ClientHandler(mHandlerThread.getLooper()); } public void onBootPhase(int phase) { Loading @@ -105,7 +103,8 @@ class TvInputHardwareManager connection.updateConfigsLocked(configs); mConnections.put(info.getDeviceId(), connection); buildInfoListLocked(); // TODO: notify if necessary mHandler.obtainMessage( ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget(); } } Loading @@ -127,7 +126,8 @@ class TvInputHardwareManager connection.resetLocked(null, null, null, null, null); mConnections.remove(deviceId); buildInfoListLocked(); // TODO: notify if necessary mHandler.obtainMessage( ListenerHandler.HARDWARE_DEVICE_REMOVED, deviceId, 0).sendToTarget(); } } Loading Loading @@ -191,7 +191,7 @@ class TvInputHardwareManager for (int i = 0; i < mHdmiStateMap.size(); ++i) { String inputId = findInputIdForHdmiPortLocked(mHdmiStateMap.keyAt(i)); if (inputId != null && inputId.equals(info.getId())) { mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE, mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, convertConnectedToState(mHdmiStateMap.valueAt(i)), 0, inputId).sendToTarget(); } Loading Loading @@ -273,7 +273,7 @@ class TvInputHardwareManager if (inputId == null) { return; } mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE, mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget(); } } Loading Loading @@ -502,20 +502,48 @@ class TvInputHardwareManager } } private class ClientHandler extends Handler { private static final int DO_SET_AVAILABLE = 1; ClientHandler(Looper looper) { super(looper); interface Listener { public void onStateChanged(String inputId, int state); public void onHardwareDeviceAdded(TvInputHardwareInfo info); public void onHardwareDeviceRemoved(int deviceId); public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice); public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice); } private class ListenerHandler extends Handler { private static final int STATE_CHANGED = 1; private static final int HARDWARE_DEVICE_ADDED = 2; private static final int HARDWARE_DEVICE_REMOVED = 3; private static final int CEC_DEVICE_ADDED = 4; private static final int CEC_DEVICE_REMOVED = 5; @Override public final void handleMessage(Message msg) { switch (msg.what) { case DO_SET_AVAILABLE: { case STATE_CHANGED: { String inputId = (String) msg.obj; int state = msg.arg1; mClient.setState(inputId, state); mListener.onStateChanged(inputId, state); break; } case HARDWARE_DEVICE_ADDED: { TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj; mListener.onHardwareDeviceAdded(info); break; } case HARDWARE_DEVICE_REMOVED: { int deviceId = msg.arg1; mListener.onHardwareDeviceRemoved(deviceId); break; } case CEC_DEVICE_ADDED: { HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj; mListener.onHdmiCecDeviceAdded(info); break; } case CEC_DEVICE_REMOVED: { HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj; mListener.onHdmiCecDeviceRemoved(info); break; } default: { Loading @@ -525,4 +553,14 @@ class TvInputHardwareManager } } } private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub { @Override public void onStatusChanged(HdmiCecDeviceInfo deviceInfo, boolean activated) { mHandler.obtainMessage( activated ? ListenerHandler.CEC_DEVICE_ADDED : ListenerHandler.CEC_DEVICE_REMOVED, 0, 0, deviceInfo).sendToTarget(); } } } Loading
media/java/android/media/tv/ITvInputService.aidl +7 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.media.tv; import android.media.tv.ITvInputServiceCallback; import android.media.tv.ITvInputSessionCallback; import android.media.tv.TvInputHardwareInfo; import android.view.InputChannel; /** Loading @@ -27,5 +28,10 @@ import android.view.InputChannel; oneway interface ITvInputService { void registerCallback(ITvInputServiceCallback callback); void unregisterCallback(in ITvInputServiceCallback callback); void createSession(in InputChannel channel, ITvInputSessionCallback callback); void createSession(in InputChannel channel, ITvInputSessionCallback callback, in String inputId); // For hardware TvInputService void notifyHardwareAdded(in TvInputHardwareInfo info); void notifyHardwareRemoved(int deviceId); }
media/java/android/media/tv/ITvInputServiceCallback.aidl +5 −4 Original line number Diff line number Diff line Loading @@ -16,13 +16,14 @@ package android.media.tv; import android.content.ComponentName; import android.media.tv.TvInputInfo; /** * Helper interface for ITvInputService to allow the TV input to notify the client when its status * has been changed. * Helper interface for ITvInputService to allow the service to notify the * TvInputManagerService. * @hide */ oneway interface ITvInputServiceCallback { void onInputStateChanged(int state); void addTvInput(in TvInputInfo inputInfo); void removeTvInput(in String inputId); }
media/java/android/media/tv/TvInputInfo.java +71 −7 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.media.tv.TvInputHardwareInfo; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; Loading Loading @@ -88,9 +90,46 @@ public final class TvInputInfo implements Parcelable { * instantiating it from the given Context and ResolveInfo. * * @param service The ResolveInfo returned from the package manager about this TV input service. * @hide */ * @hide */ public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service) throws XmlPullParserException, IOException { return createTvInputInfo(context, service, generateInputIdForComponentName( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name))); } /** * Create a new instance of the TvInputInfo class, * instantiating it from the given Context, ResolveInfo, and HdmiCecDeviceInfo. * * @param service The ResolveInfo returned from the package manager about this TV input service. * @param cecInfo The HdmiCecDeviceInfo for a HDMI CEC logical device. * @hide */ public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, HdmiCecDeviceInfo cecInfo) throws XmlPullParserException, IOException { return createTvInputInfo(context, service, generateInputIdForHdmiCec( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), cecInfo)); } /** * Create a new instance of the TvInputInfo class, * instantiating it from the given Context, ResolveInfo, and TvInputHardwareInfo. * * @param service The ResolveInfo returned from the package manager about this TV input service. * @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device. * @hide */ public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, TvInputHardwareInfo hardwareInfo) throws XmlPullParserException, IOException { return createTvInputInfo(context, service, generateInputIdForHardware( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), hardwareInfo)); } private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, String id) throws XmlPullParserException, IOException { ServiceInfo si = service.serviceInfo; PackageManager pm = context.getPackageManager(); XmlResourceParser parser = null; Loading @@ -115,7 +154,7 @@ public final class TvInputInfo implements Parcelable { "Meta-data does not start with tv-input-service tag in " + si.name); } TvInputInfo input = new TvInputInfo(context, service, null); TvInputInfo input = new TvInputInfo(context, service, id, null); TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.TvInputService); input.mSetupActivity = sa.getString( Loading Loading @@ -153,12 +192,13 @@ public final class TvInputInfo implements Parcelable { * Constructor. * * @param service The ResolveInfo returned from the package manager about this TV input service. * @hide * @param id ID of this TV input. Should be generated via generateInputId*(). * @param parentId ID of this TV input's parent input. {@code null} if none exists. */ private TvInputInfo(Context context, ResolveInfo service, String parentId) { private TvInputInfo(Context context, ResolveInfo service, String id, String parentId) { mService = service; ServiceInfo si = service.serviceInfo; mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name)); mId = id; mParentId = parentId; } Loading Loading @@ -311,12 +351,36 @@ public final class TvInputInfo implements Parcelable { * * @param name the component name for generating an input id. * @return the generated input id for the given {@code name}. * @hide */ public static final String generateInputIdForComponentName(ComponentName name) { private static final String generateInputIdForComponentName(ComponentName name) { return name.flattenToShortString(); } /** * Used to generate an input id from a ComponentName and HdmiCecDeviceInfo. * * @param name the component name for generating an input id. * @param cecInfo HdmiCecDeviceInfo describing this TV input. * @return the generated input id for the given {@code name} and {@code cecInfo}. */ private static final String generateInputIdForHdmiCec( ComponentName name, HdmiCecDeviceInfo cecInfo) { return name.flattenToShortString() + String.format("|CEC%08X%08X", cecInfo.getPhysicalAddress(), cecInfo.getLogicalAddress()); } /** * Used to generate an input id from a ComponentName and TvInputHardwareInfo * * @param name the component name for generating an input id. * @param hardwareInfo TvInputHardwareInfo describing this TV input. * @return the generated input id for the given {@code name} and {@code hardwareInfo}. */ private static final String generateInputIdForHardware( ComponentName name, TvInputHardwareInfo hardwareInfo) { return name.flattenToShortString() + String.format("|HW%d", hardwareInfo.getDeviceId()); } /** * Used to make this class parcelable. * Loading
media/java/android/media/tv/TvInputService.java +98 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.media.tv; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.Service; import android.content.ComponentName; import android.content.Context; Loading Loading @@ -104,7 +105,8 @@ public abstract class TvInputService extends Service { } @Override public void createSession(InputChannel channel, ITvInputSessionCallback cb) { public void createSession(InputChannel channel, ITvInputSessionCallback cb, String inputId) { if (channel == null) { Log.w(TAG, "Creating session without input channel"); } Loading @@ -114,8 +116,21 @@ public abstract class TvInputService extends Service { SomeArgs args = SomeArgs.obtain(); args.arg1 = channel; args.arg2 = cb; args.arg3 = inputId; mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget(); } @Override public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) { mHandler.obtainMessage(ServiceHandler.DO_ADD_TV_INPUT_FROM_HARDWARE, hardwareInfo).sendToTarget(); } @Override public void notifyHardwareRemoved(int deviceId) { mHandler.obtainMessage(ServiceHandler.DO_REMOVE_TV_INPUT_FROM_HARDWARE, deviceId, 0).sendToTarget(); } }; } Loading @@ -137,6 +152,44 @@ public abstract class TvInputService extends Service { */ public abstract Session onCreateSession(); /** * Returns a concrete implementation of {@link Session}. * <p> * May return {@code null} if this TV input service fails to create a session for some reason. * </p> * @param inputId The ID of the TV input associated with the session. * * @hide */ @SystemApi public Session onCreateSession(String inputId) { return onCreateSession(); } /** * Returns a new TvInputInfo object if this service is responsible for {@code hardwareInfo}; * otherwise, return {@code null}. Override to modify default behavior of ignoring all input. * * @param hardwareInfo TvInputHardwareInfo object just added. * * @hide */ @SystemApi public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) { return null; } /** * Returns the input ID for {@code deviceId} if it is handled by this service; * otherwise, return {@code null}. Override to modify default behavior of ignoring all input. * * @hide */ @SystemApi public String onHardwareRemoved(int deviceId) { return null; } /** * Base class for derived classes to implement to provide a TV input session. */ Loading Loading @@ -715,6 +768,32 @@ public abstract class TvInputService extends Service { @SuppressLint("HandlerLeak") private final class ServiceHandler extends Handler { private static final int DO_CREATE_SESSION = 1; private static final int DO_ADD_TV_INPUT_FROM_HARDWARE = 2; private static final int DO_REMOVE_TV_INPUT_FROM_HARDWARE = 3; private void broadcastAddTvInput(TvInputInfo inputInfo) { int n = mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { try { mCallbacks.getBroadcastItem(i).addTvInput(inputInfo); } catch (RemoteException e) { Log.e(TAG, "Error while broadcasting: " + e); } } mCallbacks.finishBroadcast(); } private void broadcastRemoveTvInput(String inputId) { int n = mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { try { mCallbacks.getBroadcastItem(i).removeTvInput(inputId); } catch (RemoteException e) { Log.e(TAG, "Error while broadcasting: " + e); } } mCallbacks.finishBroadcast(); } @Override public final void handleMessage(Message msg) { Loading @@ -723,8 +802,9 @@ public abstract class TvInputService extends Service { SomeArgs args = (SomeArgs) msg.obj; InputChannel channel = (InputChannel) args.arg1; ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2; String inputId = (String) args.arg3; try { Session sessionImpl = onCreateSession(); Session sessionImpl = onCreateSession(inputId); if (sessionImpl == null) { // Failed to create a session. cb.onSessionCreated(null); Loading @@ -740,6 +820,22 @@ public abstract class TvInputService extends Service { args.recycle(); return; } case DO_ADD_TV_INPUT_FROM_HARDWARE: { TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj; TvInputInfo inputInfo = onHardwareAdded(hardwareInfo); if (inputInfo != null) { broadcastAddTvInput(inputInfo); } return; } case DO_REMOVE_TV_INPUT_FROM_HARDWARE: { int deviceId = msg.arg1; String inputId = onHardwareRemoved(deviceId); if (inputId != null) { broadcastRemoveTvInput(inputId); } return; } default: { Log.w(TAG, "Unhandled message code: " + msg.what); return; Loading
services/core/java/com/android/server/tv/TvInputHardwareManager.java +59 −21 Original line number Diff line number Diff line Loading @@ -20,8 +20,10 @@ import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED; import android.content.Context; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiHotplugEvent; import android.hardware.hdmi.IHdmiDeviceEventListener; import android.media.AudioDevicePort; import android.media.AudioManager; import android.media.AudioPatch; Loading @@ -33,7 +35,6 @@ import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvInputInfo; import android.media.tv.TvStreamConfig; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; Loading Loading @@ -66,27 +67,24 @@ class TvInputHardwareManager private final SparseArray<Connection> mConnections = new SparseArray<Connection>(); private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>(); private final Context mContext; private final TvInputManagerService.Client mClient; private final Listener mListener; private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>(); private final AudioManager mAudioManager; private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray(); // TODO: Should handle INACTIVE case. private final SparseArray<TvInputInfo> mTvInputInfoMap = new SparseArray<TvInputInfo>(); private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener(); // Calls to mClient should happen here. private final HandlerThread mHandlerThread = new HandlerThread(TAG); private final Handler mHandler; // Calls to mListener should happen here. private final Handler mHandler = new ListenerHandler(); private final Object mLock = new Object(); public TvInputHardwareManager(Context context, TvInputManagerService.Client client) { public TvInputHardwareManager(Context context, Listener listener) { mContext = context; mClient = client; mListener = listener; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mHal.init(); mHandlerThread.start(); mHandler = new ClientHandler(mHandlerThread.getLooper()); } public void onBootPhase(int phase) { Loading @@ -105,7 +103,8 @@ class TvInputHardwareManager connection.updateConfigsLocked(configs); mConnections.put(info.getDeviceId(), connection); buildInfoListLocked(); // TODO: notify if necessary mHandler.obtainMessage( ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget(); } } Loading @@ -127,7 +126,8 @@ class TvInputHardwareManager connection.resetLocked(null, null, null, null, null); mConnections.remove(deviceId); buildInfoListLocked(); // TODO: notify if necessary mHandler.obtainMessage( ListenerHandler.HARDWARE_DEVICE_REMOVED, deviceId, 0).sendToTarget(); } } Loading Loading @@ -191,7 +191,7 @@ class TvInputHardwareManager for (int i = 0; i < mHdmiStateMap.size(); ++i) { String inputId = findInputIdForHdmiPortLocked(mHdmiStateMap.keyAt(i)); if (inputId != null && inputId.equals(info.getId())) { mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE, mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, convertConnectedToState(mHdmiStateMap.valueAt(i)), 0, inputId).sendToTarget(); } Loading Loading @@ -273,7 +273,7 @@ class TvInputHardwareManager if (inputId == null) { return; } mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE, mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget(); } } Loading Loading @@ -502,20 +502,48 @@ class TvInputHardwareManager } } private class ClientHandler extends Handler { private static final int DO_SET_AVAILABLE = 1; ClientHandler(Looper looper) { super(looper); interface Listener { public void onStateChanged(String inputId, int state); public void onHardwareDeviceAdded(TvInputHardwareInfo info); public void onHardwareDeviceRemoved(int deviceId); public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice); public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice); } private class ListenerHandler extends Handler { private static final int STATE_CHANGED = 1; private static final int HARDWARE_DEVICE_ADDED = 2; private static final int HARDWARE_DEVICE_REMOVED = 3; private static final int CEC_DEVICE_ADDED = 4; private static final int CEC_DEVICE_REMOVED = 5; @Override public final void handleMessage(Message msg) { switch (msg.what) { case DO_SET_AVAILABLE: { case STATE_CHANGED: { String inputId = (String) msg.obj; int state = msg.arg1; mClient.setState(inputId, state); mListener.onStateChanged(inputId, state); break; } case HARDWARE_DEVICE_ADDED: { TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj; mListener.onHardwareDeviceAdded(info); break; } case HARDWARE_DEVICE_REMOVED: { int deviceId = msg.arg1; mListener.onHardwareDeviceRemoved(deviceId); break; } case CEC_DEVICE_ADDED: { HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj; mListener.onHdmiCecDeviceAdded(info); break; } case CEC_DEVICE_REMOVED: { HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj; mListener.onHdmiCecDeviceRemoved(info); break; } default: { Loading @@ -525,4 +553,14 @@ class TvInputHardwareManager } } } private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub { @Override public void onStatusChanged(HdmiCecDeviceInfo deviceInfo, boolean activated) { mHandler.obtainMessage( activated ? ListenerHandler.CEC_DEVICE_ADDED : ListenerHandler.CEC_DEVICE_REMOVED, 0, 0, deviceInfo).sendToTarget(); } } }