diff options
| -rw-r--r-- | Android.mk | 1 | ||||
| -rw-r--r-- | api/current.txt | 9 | ||||
| -rw-r--r-- | media/java/android/media/tv/ITvInputClient.aidl | 1 | ||||
| -rw-r--r-- | media/java/android/media/tv/ITvInputManager.aidl | 16 | ||||
| -rw-r--r-- | media/java/android/media/tv/ITvInputManagerCallback.aidl | 25 | ||||
| -rw-r--r-- | media/java/android/media/tv/ITvInputServiceCallback.aidl | 2 | ||||
| -rw-r--r-- | media/java/android/media/tv/TvInputInfo.java | 13 | ||||
| -rw-r--r-- | media/java/android/media/tv/TvInputManager.java | 165 | ||||
| -rw-r--r-- | media/java/android/media/tv/TvInputService.java | 33 | ||||
| -rw-r--r-- | services/core/java/com/android/server/tv/TvInputHardwareManager.java | 156 | ||||
| -rw-r--r-- | services/core/java/com/android/server/tv/TvInputManagerService.java | 268 |
11 files changed, 432 insertions, 257 deletions
diff --git a/Android.mk b/Android.mk index a823ba02cb85..8a50ae8456e8 100644 --- a/Android.mk +++ b/Android.mk @@ -336,6 +336,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/tv/ITvInputHardware.aidl \ media/java/android/media/tv/ITvInputHardwareCallback.aidl \ media/java/android/media/tv/ITvInputManager.aidl \ + media/java/android/media/tv/ITvInputManagerCallback.aidl \ media/java/android/media/tv/ITvInputService.aidl \ media/java/android/media/tv/ITvInputServiceCallback.aidl \ media/java/android/media/tv/ITvInputSession.aidl \ diff --git a/api/current.txt b/api/current.txt index fcc02d01441e..1f980b30bdf8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16425,8 +16425,13 @@ package android.media.tv { } public final class TvInputManager { - method public boolean getAvailability(java.lang.String); + method public int getInputState(java.lang.String); method public java.util.List<android.media.tv.TvInputInfo> getTvInputList(); + method public void registerListener(android.media.tv.TvInputManager.TvInputListener, android.os.Handler); + method public void unregisterListener(android.media.tv.TvInputManager.TvInputListener); + field public static final int INPUT_STATE_CONNECTED = 0; // 0x0 + field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1 + field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2 field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3 field public static final int VIDEO_UNAVAILABLE_REASON_TUNE = 1; // 0x1 field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0 @@ -16435,7 +16440,7 @@ package android.media.tv { public static abstract class TvInputManager.TvInputListener { ctor public TvInputManager.TvInputListener(); - method public void onAvailabilityChanged(java.lang.String, boolean); + method public void onInputStateChanged(java.lang.String, int); } public abstract class TvInputService extends android.app.Service { diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl index cac8a14687e6..423e317ede69 100644 --- a/media/java/android/media/tv/ITvInputClient.aidl +++ b/media/java/android/media/tv/ITvInputClient.aidl @@ -30,7 +30,6 @@ import android.view.InputChannel; */ oneway interface ITvInputClient { void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq); - void onAvailabilityChanged(in String inputId, boolean isAvailable); void onSessionReleased(int seq); void onSessionEvent(in String name, in Bundle args, int seq); void onChannelRetuned(in Uri channelUri, int seq); diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index 9a6a6486ab0c..6a0c592aeff4 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -18,9 +18,10 @@ package android.media.tv; import android.content.ComponentName; import android.graphics.Rect; +import android.media.tv.ITvInputClient; import android.media.tv.ITvInputHardware; import android.media.tv.ITvInputHardwareCallback; -import android.media.tv.ITvInputClient; +import android.media.tv.ITvInputManagerCallback; import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvInputInfo; import android.media.tv.TvTrackInfo; @@ -34,10 +35,8 @@ import android.view.Surface; interface ITvInputManager { List<TvInputInfo> getTvInputList(int userId); - boolean getAvailability(in ITvInputClient client, in String inputId, int userId); - - void registerCallback(in ITvInputClient client, in String inputId, int userId); - void unregisterCallback(in ITvInputClient client, in String inputId, int userId); + void registerCallback(in ITvInputManagerCallback callback, int userId); + void unregisterCallback(in ITvInputManagerCallback callback, int userId); void createSession(in ITvInputClient client, in String inputId, int seq, int userId); void releaseSession(in IBinder sessionToken, int userId); @@ -56,7 +55,12 @@ interface ITvInputManager { // For TV input hardware binding List<TvInputHardwareInfo> getHardwareList(); + /* + * All TvInputServices which want to use hardware must call this method on + * BOOT_COMPLETE. + */ + void registerTvInputInfo(in TvInputInfo info, int deviceId); ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback, - int userId); + in TvInputInfo info, int userId); void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId); } diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl new file mode 100644 index 000000000000..5c8a0a318629 --- /dev/null +++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl @@ -0,0 +1,25 @@ +/* + * 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 android.media.tv; + +/** + * Interface to receive callbacks from ITvInputManager regardless of sessions. + * @hide + */ +oneway interface ITvInputManagerCallback { + void onInputStateChanged(in String inputId, int state); +} diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl index c9484dd85185..1fdb8c57a623 100644 --- a/media/java/android/media/tv/ITvInputServiceCallback.aidl +++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl @@ -24,5 +24,5 @@ import android.content.ComponentName; * @hide */ oneway interface ITvInputServiceCallback { - void onAvailabilityChanged(in String inputId, boolean isAvailable); + void onInputStateChanged(int state); } diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 7b8f2ecd2a41..5624f3ec0bbb 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -80,7 +80,7 @@ public final class TvInputInfo implements Parcelable { // Attributes from XML meta data. private String mSetupActivity; private String mSettingsActivity; - private int mType; + private int mType = TYPE_VIRTUAL; /** * Create a new instance of the TvInputInfo class, @@ -128,10 +128,13 @@ public final class TvInputInfo implements Parcelable { Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for " + si.name); } - input.mType = sa.getInt( - com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL); - if (DEBUG) { - Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name); + if (pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, si.packageName) + == PackageManager.PERMISSION_GRANTED) { + input.mType = sa.getInt( + com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL); + if (DEBUG) { + Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name); + } } sa.recycle(); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 867b0db6ab6c..79a83b0c6b2f 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -24,6 +24,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.util.ArrayMap; import android.util.Log; import android.util.Pools.Pool; import android.util.Pools.SimplePool; @@ -37,6 +38,7 @@ import android.view.View; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -65,11 +67,43 @@ public final class TvInputManager { */ public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; + /** + * The TV input is connected. + * <p> + * State for {@link #getInputState} and {@link + * TvInputManager.TvInputListener#onInputStateChanged}. + * </p> + */ + public static final int INPUT_STATE_CONNECTED = 0; + /** + * The TV input is connected but in standby mode. It would take a while until it becomes + * fully ready. + * <p> + * State for {@link #getInputState} and {@link + * TvInputManager.TvInputListener#onInputStateChanged}. + * </p> + */ + public static final int INPUT_STATE_CONNECTED_STANDBY = 1; + /** + * The TV input is disconnected. + * <p> + * State for {@link #getInputState} and {@link + * TvInputManager.TvInputListener#onInputStateChanged}. + * </p> + */ + public static final int INPUT_STATE_DISCONNECTED = 2; + private final ITvInputManager mService; - // A mapping from an input to the list of its TvInputListenerRecords. - private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap = - new HashMap<String, List<TvInputListenerRecord>>(); + private final Object mLock = new Object(); + + // @GuardedBy(mLock) + private final List<TvInputListenerRecord> mTvInputListenerRecordsList = + new LinkedList<TvInputListenerRecord>(); + + // A mapping from TV input ID to the state of corresponding input. + // @GuardedBy(mLock) + private final Map<String, Integer> mStateMap = new ArrayMap<String, Integer>(); // A mapping from the sequence number of a session to its SessionCallbackRecord. private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap = @@ -81,6 +115,8 @@ public final class TvInputManager { private final ITvInputClient mClient; + private final ITvInputManagerCallback mCallback; + private final int mUserId; /** @@ -242,13 +278,17 @@ public final class TvInputManager { */ public abstract static class TvInputListener { /** - * This is called when the availability status of a given TV input is changed. + * This is called when the state of a given TV input is changed. * * @param inputId the id of the TV input. - * @param isAvailable {@code true} if the given TV input is available to show TV programs. - * {@code false} otherwise. + * @param state state of the TV input. The value is one of the following: + * <ul> + * <li>{@link TvInputManager#INPUT_STATE_CONNECTED} + * <li>{@link TvInputManager#INPUT_STATE_CONNECTED_STANDBY} + * <li>{@link TvInputManager#INPUT_STATE_DISCONNECTED} + * </ul> */ - public void onAvailabilityChanged(String inputId, boolean isAvailable) { + public void onInputStateChanged(String inputId, int state) { } } @@ -265,11 +305,11 @@ public final class TvInputManager { return mListener; } - public void postAvailabilityChanged(final String inputId, final boolean isAvailable) { + public void postStateChanged(final String inputId, final int state) { mHandler.post(new Runnable() { @Override public void run() { - mListener.onAvailabilityChanged(inputId, isAvailable); + mListener.onInputStateChanged(inputId, state); } }); } @@ -373,22 +413,23 @@ public final class TvInputManager { record.postSessionEvent(eventType, eventArgs); } } - + }; + mCallback = new ITvInputManagerCallback.Stub() { @Override - public void onAvailabilityChanged(String inputId, boolean isAvailable) { - synchronized (mTvInputListenerRecordsMap) { - List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId); - if (records == null) { - // Silently ignore - no listener is registered yet. - return; - } - int recordsCount = records.size(); - for (int i = 0; i < recordsCount; i++) { - records.get(i).postAvailabilityChanged(inputId, isAvailable); + public void onInputStateChanged(String inputId, int state) { + synchronized (mLock) { + mStateMap.put(inputId, state); + for (TvInputListenerRecord record : mTvInputListenerRecordsList) { + record.postStateChanged(inputId, state); } } } }; + try { + mService.registerCallback(mCallback, mUserId); + } catch (RemoteException e) { + Log.e(TAG, "mService.registerCallback failed: " + e); + } } /** @@ -405,98 +446,66 @@ public final class TvInputManager { } /** - * Returns the availability of a given TV input. + * Returns the state of a given TV input. It retuns one of the following: + * <ul> + * <li>{@link #INPUT_STATE_CONNECTED} + * <li>{@link #INPUT_STATE_CONNECTED_STANDBY} + * <li>{@link #INPUT_STATE_DISCONNECTED} + * </ul> * * @param inputId the id of the TV input. - * @throws IllegalArgumentException if the argument is {@code null}. - * @throws IllegalStateException If there is no {@link TvInputListener} registered on the given - * TV input. + * @throws IllegalArgumentException if the argument is {@code null} or if there is no + * {@link TvInputInfo} corresponding to {@code inputId}. */ - public boolean getAvailability(String inputId) { + public int getInputState(String inputId) { if (inputId == null) { throw new IllegalArgumentException("id cannot be null"); } - synchronized (mTvInputListenerRecordsMap) { - List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId); - if (records == null || records.size() == 0) { - throw new IllegalStateException("At least one listener should be registered."); + synchronized (mLock) { + Integer state = mStateMap.get(inputId); + if (state == null) { + throw new IllegalArgumentException("Unrecognized input ID: " + inputId); } - } - try { - return mService.getAvailability(mClient, inputId, mUserId); - } catch (RemoteException e) { - throw new RuntimeException(e); + return state.intValue(); } } /** - * Registers a {@link TvInputListener} for a given TV input. + * Registers a {@link TvInputListener}. * - * @param inputId the id of the TV input. - * @param listener a listener used to monitor status of the given TV input. + * @param listener a listener used to monitor status of the TV inputs. * @param handler a {@link Handler} that the status change will be delivered to. * @throws IllegalArgumentException if any of the arguments is {@code null}. - * @hide */ - public void registerListener(String inputId, TvInputListener listener, Handler handler) { - if (inputId == null) { - throw new IllegalArgumentException("id cannot be null"); - } + public void registerListener(TvInputListener listener, Handler handler) { if (listener == null) { throw new IllegalArgumentException("listener cannot be null"); } if (handler == null) { throw new IllegalArgumentException("handler cannot be null"); } - synchronized (mTvInputListenerRecordsMap) { - List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId); - if (records == null) { - records = new ArrayList<TvInputListenerRecord>(); - mTvInputListenerRecordsMap.put(inputId, records); - try { - mService.registerCallback(mClient, inputId, mUserId); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - records.add(new TvInputListenerRecord(listener, handler)); + synchronized (mLock) { + mTvInputListenerRecordsList.add(new TvInputListenerRecord(listener, handler)); } } /** - * Unregisters the existing {@link TvInputListener} for a given TV input. + * Unregisters the existing {@link TvInputListener}. * - * @param inputId the id of the TV input. - * @param listener the existing listener to remove for the given TV input. + * @param listener the existing listener to remove. * @throws IllegalArgumentException if any of the arguments is {@code null}. - * @hide */ - public void unregisterListener(String inputId, final TvInputListener listener) { - if (inputId == null) { - throw new IllegalArgumentException("id cannot be null"); - } + public void unregisterListener(final TvInputListener listener) { if (listener == null) { throw new IllegalArgumentException("listener cannot be null"); } - synchronized (mTvInputListenerRecordsMap) { - List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId); - if (records == null) { - Log.e(TAG, "No listener found for " + inputId); - return; - } - for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) { + synchronized (mLock) { + for (Iterator<TvInputListenerRecord> it = mTvInputListenerRecordsList.iterator(); + it.hasNext(); ) { TvInputListenerRecord record = it.next(); if (record.getListener() == listener) { it.remove(); - } - } - if (records.isEmpty()) { - try { - mService.unregisterCallback(mClient, inputId, mUserId); - } catch (RemoteException e) { - throw new RuntimeException(e); - } finally { - mTvInputListenerRecordsMap.remove(inputId); + break; } } } diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index a994f5472271..320632022a07 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -82,20 +82,9 @@ public abstract class TvInputService extends Service { */ public static final String SERVICE_META_DATA = "android.media.tv.input"; - private String mId; private final Handler mHandler = new ServiceHandler(); private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks = new RemoteCallbackList<ITvInputServiceCallback>(); - // STOPSHIP: Redesign the API around the availability change. For now, the service will be - // always available. - private final boolean mAvailable = true; - - @Override - public void onCreate() { - super.onCreate(); - mId = TvInputInfo.generateInputIdForComponentName( - new ComponentName(getPackageName(), getClass().getName())); - } @Override public final IBinder onBind(Intent intent) { @@ -104,13 +93,6 @@ public abstract class TvInputService extends Service { public void registerCallback(ITvInputServiceCallback cb) { if (cb != null) { mCallbacks.register(cb); - // The first time a callback is registered, the service needs to report its - // availability status so that the system can know its initial value. - try { - cb.onAvailabilityChanged(mId, mAvailable); - } catch (RemoteException e) { - Log.e(TAG, "error in onAvailabilityChanged", e); - } } } @@ -733,7 +715,6 @@ 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_BROADCAST_AVAILABILITY_CHANGE = 2; @Override public final void handleMessage(Message msg) { @@ -759,20 +740,6 @@ public abstract class TvInputService extends Service { args.recycle(); return; } - case DO_BROADCAST_AVAILABILITY_CHANGE: { - boolean isAvailable = (Boolean) msg.obj; - int n = mCallbacks.beginBroadcast(); - try { - for (int i = 0; i < n; i++) { - mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable); - } - } catch (RemoteException e) { - Log.e(TAG, "Unexpected exception", e); - } finally { - mCallbacks.finishBroadcast(); - } - return; - } default: { Log.w(TAG, "Unhandled message code: " + msg.what); return; diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index efe543beb2df..8f237db51078 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -16,7 +16,12 @@ package com.android.server.tv; +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.HdmiControlManager; +import android.hardware.hdmi.HdmiHotplugEvent; import android.media.AudioDevicePort; import android.media.AudioManager; import android.media.AudioPatch; @@ -25,14 +30,22 @@ import android.media.AudioPortConfig; import android.media.tv.ITvInputHardware; import android.media.tv.ITvInputHardwareCallback; 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; import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.KeyEvent; import android.view.Surface; +import com.android.server.SystemService; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -46,23 +59,42 @@ import java.util.Set; * * @hide */ -class TvInputHardwareManager implements TvInputHal.Callback { +class TvInputHardwareManager + implements TvInputHal.Callback, HdmiControlManager.HotplugEventListener { 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 TvInputManagerService.Client mClient; 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>(); + + // Calls to mClient should happen here. + private final HandlerThread mHandlerThread = new HandlerThread(TAG); + private final Handler mHandler; private final Object mLock = new Object(); - public TvInputHardwareManager(Context context) { + public TvInputHardwareManager(Context context, TvInputManagerService.Client client) { mContext = context; + mClient = client; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - // TODO(hdmi): mHdmiManager = mContext.getSystemService(...); - // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient(); mHal.init(); + + mHandlerThread.start(); + mHandler = new ClientHandler(mHandlerThread.getLooper()); + } + + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + HdmiControlManager hdmiControlManager = + (HdmiControlManager) mContext.getSystemService(Context.HDMI_CONTROL_SERVICE); + hdmiControlManager.addHotplugEventListener(this); + } } @Override @@ -80,7 +112,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { private void buildInfoListLocked() { mInfoList.clear(); for (int i = 0; i < mConnections.size(); ++i) { - mInfoList.add(mConnections.valueAt(i).getInfoLocked()); + mInfoList.add(mConnections.valueAt(i).getHardwareInfoLocked()); } } @@ -92,7 +124,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId); return; } - connection.resetLocked(null, null, null, null); + connection.resetLocked(null, null, null, null, null); mConnections.remove(deviceId); buildInfoListLocked(); // TODO: notify if necessary @@ -136,6 +168,37 @@ class TvInputHardwareManager implements TvInputHal.Callback { return false; } + private int convertConnectedToState(boolean connected) { + if (connected) { + return INPUT_STATE_CONNECTED; + } else { + return INPUT_STATE_DISCONNECTED; + } + } + + public void registerTvInputInfo(TvInputInfo info, int deviceId) { + if (info.getType() == TvInputInfo.TYPE_VIRTUAL) { + throw new IllegalArgumentException("info (" + info + ") has virtual type."); + } + synchronized (mLock) { + if (mTvInputInfoMap.indexOfKey(deviceId) >= 0) { + Slog.w(TAG, "Trying to override previous registration: old = " + + mTvInputInfoMap.get(deviceId) + ":" + deviceId + ", new = " + + info + ":" + deviceId); + } + mTvInputInfoMap.put(deviceId, info); + + 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, + convertConnectedToState(mHdmiStateMap.valueAt(i)), 0, + inputId).sendToTarget(); + } + } + } + } + /** * 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, @@ -143,7 +206,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { * release is notified via ITvInputHardwareCallback.onReleased(). */ public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback, - int callingUid, int resolvedUserId) { + TvInputInfo info, int callingUid, int resolvedUserId) { if (callback == null) { throw new NullPointerException(); } @@ -154,14 +217,15 @@ class TvInputHardwareManager implements TvInputHal.Callback { return null; } if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) { - TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked()); + TvInputHardwareImpl hardware = + new TvInputHardwareImpl(connection.getHardwareInfoLocked()); try { callback.asBinder().linkToDeath(connection, 0); } catch (RemoteException e) { hardware.release(); return null; } - connection.resetLocked(hardware, callback, callingUid, resolvedUserId); + connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId); } return connection.getHardwareLocked(); } @@ -182,26 +246,55 @@ class TvInputHardwareManager implements TvInputHal.Callback { || checkUidChangedLocked(connection, callingUid, resolvedUserId)) { return; } - connection.resetLocked(null, null, null, null); + connection.resetLocked(null, null, null, null, null); + } + } + + private String findInputIdForHdmiPortLocked(int port) { + for (TvInputHardwareInfo hardwareInfo : mInfoList) { + if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI + && hardwareInfo.getHdmiPortId() == port) { + TvInputInfo info = mTvInputInfoMap.get(hardwareInfo.getDeviceId()); + return (info == null) ? null : info.getId(); + } + } + return null; + } + + // HdmiControlManager.HotplugEventListener implementation. + + @Override + public void onReceived(HdmiHotplugEvent event) { + String inputId = null; + + synchronized (mLock) { + mHdmiStateMap.put(event.getPort(), event.isConnected()); + inputId = findInputIdForHdmiPortLocked(event.getPort()); + if (inputId == null) { + return; + } + mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE, + convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget(); } } private class Connection implements IBinder.DeathRecipient { - private final TvInputHardwareInfo mInfo; + private final TvInputHardwareInfo mHardwareInfo; + private TvInputInfo 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; + public Connection(TvInputHardwareInfo hardwareInfo) { + mHardwareInfo = hardwareInfo; } // *Locked methods assume TvInputHardwareManager.mLock is held. - public void resetLocked(TvInputHardwareImpl hardware, - ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) { + public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback, + TvInputInfo info, Integer callingUid, Integer resolvedUserId) { if (mHardware != null) { try { mCallback.onReleased(); @@ -212,6 +305,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } mHardware = hardware; mCallback = callback; + mInfo = info; mCallingUid = callingUid; mResolvedUserId = resolvedUserId; @@ -228,7 +322,11 @@ class TvInputHardwareManager implements TvInputHal.Callback { mConfigs = configs; } - public TvInputHardwareInfo getInfoLocked() { + public TvInputHardwareInfo getHardwareInfoLocked() { + return mHardwareInfo; + } + + public TvInputInfo getInfoLocked() { return mInfo; } @@ -255,7 +353,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { @Override public void binderDied() { synchronized (mLock) { - resetLocked(null, null, null, null); + resetLocked(null, null, null, null, null); } } } @@ -403,4 +501,28 @@ class TvInputHardwareManager implements TvInputHal.Callback { return false; } } + + private class ClientHandler extends Handler { + private static final int DO_SET_AVAILABLE = 1; + + ClientHandler(Looper looper) { + super(looper); + } + + @Override + public final void handleMessage(Message msg) { + switch (msg.what) { + case DO_SET_AVAILABLE: { + String inputId = (String) msg.obj; + int state = msg.arg1; + mClient.setState(inputId, state); + break; + } + default: { + Slog.w(TAG, "Unhandled message: " + msg); + break; + } + } + } + } } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 5e95af436871..20fdefa32c7a 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -16,6 +16,9 @@ package com.android.server.tv; +import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; +import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED; + import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -38,6 +41,7 @@ import android.media.tv.ITvInputClient; import android.media.tv.ITvInputHardware; import android.media.tv.ITvInputHardwareCallback; import android.media.tv.ITvInputManager; +import android.media.tv.ITvInputManagerCallback; import android.media.tv.ITvInputService; import android.media.tv.ITvInputServiceCallback; import android.media.tv.ITvInputSession; @@ -109,7 +113,7 @@ public final class TvInputManagerService extends SystemService { mContentResolver = context.getContentResolver(); mLogHandler = new LogHandler(IoThread.get().getLooper()); - mTvInputHardwareManager = new TvInputHardwareManager(context); + mTvInputHardwareManager = new TvInputHardwareManager(context, new Client()); synchronized (mLock) { mUserStates.put(mCurrentUserId, new UserState()); @@ -129,6 +133,7 @@ public final class TvInputManagerService extends SystemService { buildTvInputListLocked(mCurrentUserId); } } + mTvInputHardwareManager.onBootPhase(phase); } private void registerBroadcastReceivers() { @@ -144,7 +149,7 @@ public final class TvInputManagerService extends SystemService { public void onPackageRemoved(String packageName, int uid) { synchronized (mLock) { UserState userState = getUserStateLocked(mCurrentUserId); - if (!userState.packageList.contains(packageName)) { + if (!userState.packageSet.contains(packageName)) { // Not a TV input package. return; } @@ -198,8 +203,11 @@ public final class TvInputManagerService extends SystemService { private void buildTvInputListLocked(int userId) { UserState userState = getUserStateLocked(userId); - userState.inputMap.clear(); - userState.packageList.clear(); + + Map<String, TvInputState> oldInputMap = userState.inputMap; + userState.inputMap = new HashMap<String, TvInputState>(); + + userState.packageSet.clear(); if (DEBUG) Slog.d(TAG, "buildTvInputList"); PackageManager pm = mContext.getPackageManager(); @@ -216,8 +224,13 @@ public final class TvInputManagerService extends SystemService { try { TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri); if (DEBUG) Slog.d(TAG, "add " + info.getId()); - userState.inputMap.put(info.getId(), info); - userState.packageList.add(si.packageName); + TvInputState state = oldInputMap.get(info.getId()); + if (state == null) { + state = new TvInputState(); + } + userState.inputMap.put(info.getId(), state); + state.mInfo = info; + userState.packageSet.add(si.packageName); // Reconnect the service if existing input is updated. updateServiceConnectionLocked(info.getId(), userId); @@ -225,6 +238,7 @@ public final class TvInputManagerService extends SystemService { Slog.e(TAG, "Can't load TV input " + si.name, e); } } + oldInputMap.clear(); } private void switchUser(int userId) { @@ -359,7 +373,7 @@ public final class TvInputManagerService extends SystemService { } Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent( - userState.inputMap.get(inputId).getComponent()); + userState.inputMap.get(inputId).mInfo.getComponent()); // Binding service may fail if the service is updating. // In that case, the connection will be revived in buildTvInputListLocked called by // onSomePackagesChanged. @@ -588,7 +602,7 @@ public final class TvInputManagerService extends SystemService { updateServiceConnectionLocked(sessionState.mInputId, userId); } - private void unregisterCallbackInternalLocked(IBinder clientToken, String inputId, + private void unregisterClientInternalLocked(IBinder clientToken, String inputId, int userId) { UserState userState = getUserStateLocked(userId); ClientState clientState = userState.clientStateMap.get(clientToken); @@ -623,17 +637,46 @@ public final class TvInputManagerService extends SystemService { } } - private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) { - for (IBinder clientToken : serviceState.mClientTokens) { + private void notifyStateChangedLocked(UserState userState, String inputId, + int state, ITvInputManagerCallback targetCallback) { + if (DEBUG) { + Slog.d(TAG, "notifyStateChangedLocked: inputId = " + inputId + + "; state = " + state); + } + if (targetCallback == null) { + for (ITvInputManagerCallback callback : userState.callbackSet) { + try { + callback.onInputStateChanged(inputId, state); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report state change to callback."); + } + } + } else { try { - ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged( - serviceState.mTvInputInfo.getId(), serviceState.mAvailable); + targetCallback.onInputStateChanged(inputId, state); } catch (RemoteException e) { - Slog.e(TAG, "error in onAvailabilityChanged", e); + Slog.e(TAG, "Failed to report state change to callback."); } } } + private void setStateLocked(String inputId, int state, int userId) { + UserState userState = getUserStateLocked(userId); + TvInputState inputState = userState.inputMap.get(inputId); + ServiceState serviceState = userState.serviceStateMap.get(inputId); + int oldState = inputState.mState; + inputState.mState = state; + boolean isStateEmpty = serviceState.mClientTokens.isEmpty() + && serviceState.mSessionTokens.isEmpty(); + if (serviceState != null && serviceState.mService == null && !isStateEmpty) { + // We don't notify state change while reconnecting. It should remain disconnected. + return; + } + if (oldState != state) { + notifyStateChangedLocked(userState, inputId, state, null); + } + } + private final class BinderService extends ITvInputManager.Stub { @Override public List<TvInputInfo> getTvInputList(int userId) { @@ -643,80 +686,29 @@ public final class TvInputManagerService extends SystemService { try { synchronized (mLock) { UserState userState = getUserStateLocked(resolvedUserId); - return new ArrayList<TvInputInfo>(userState.inputMap.values()); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override - public boolean getAvailability(final ITvInputClient client, final String inputId, - int userId) { - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), - Binder.getCallingUid(), userId, "getAvailability"); - final long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - UserState userState = getUserStateLocked(resolvedUserId); - ServiceState serviceState = userState.serviceStateMap.get(inputId); - if (serviceState != null) { - // We already know the status of this input service. Return the cached - // status. - return serviceState.mAvailable; + List<TvInputInfo> inputList = new ArrayList<TvInputInfo>(); + for (TvInputState state : userState.inputMap.values()) { + inputList.add(state.mInfo); } + return inputList; } } finally { Binder.restoreCallingIdentity(identity); } - // STOPSHIP: Redesign the API around the availability change. For now, the service - // will be always available. - return true; } @Override - public void registerCallback(final ITvInputClient client, final String inputId, - int userId) { + public void registerCallback(final ITvInputManagerCallback callback, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "registerCallback"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { - // Create a new service callback and add it to the callback map of the current - // service. UserState userState = getUserStateLocked(resolvedUserId); - ServiceState serviceState = userState.serviceStateMap.get(inputId); - if (serviceState == null) { - serviceState = new ServiceState( - userState.inputMap.get(inputId), resolvedUserId); - userState.serviceStateMap.put(inputId, serviceState); - } - IBinder clientToken = client.asBinder(); - if (!serviceState.mClientTokens.contains(clientToken)) { - serviceState.mClientTokens.add(clientToken); - } - - ClientState clientState = userState.clientStateMap.get(clientToken); - if (clientState == null) { - clientState = createClientStateLocked(clientToken, resolvedUserId); - } - if (!clientState.mInputIds.contains(inputId)) { - clientState.mInputIds.add(inputId); - } - - if (serviceState.mService != null) { - if (serviceState.mCallback != null) { - // We already handled. - return; - } - serviceState.mCallback = new ServiceCallback(resolvedUserId); - try { - serviceState.mService.registerCallback(serviceState.mCallback); - } catch (RemoteException e) { - Slog.e(TAG, "error in registerCallback", e); - } - } else { - updateServiceConnectionLocked(inputId, resolvedUserId); + userState.callbackSet.add(callback); + for (TvInputState state : userState.inputMap.values()) { + notifyStateChangedLocked(userState, state.mInfo.getId(), + state.mState, callback); } } } finally { @@ -725,13 +717,14 @@ public final class TvInputManagerService extends SystemService { } @Override - public void unregisterCallback(ITvInputClient client, String inputId, int userId) { + public void unregisterCallback(ITvInputManagerCallback callback, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "unregisterCallback"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { - unregisterCallbackInternalLocked(client.asBinder(), inputId, resolvedUserId); + UserState userState = getUserStateLocked(resolvedUserId); + userState.callbackSet.remove(callback); } } finally { Binder.restoreCallingIdentity(identity); @@ -751,7 +744,7 @@ public final class TvInputManagerService extends SystemService { ServiceState serviceState = userState.serviceStateMap.get(inputId); if (serviceState == null) { serviceState = new ServiceState( - userState.inputMap.get(inputId), resolvedUserId); + userState.inputMap.get(inputId).mInfo, resolvedUserId); userState.serviceStateMap.put(inputId, serviceState); } // Send a null token immediately while reconnecting. @@ -868,7 +861,7 @@ public final class TvInputManagerService extends SystemService { } // Create a log entry and fill it later. - String packageName = userState.inputMap.get(sessionState.mInputId) + String packageName = userState.inputMap.get(sessionState.mInputId).mInfo .getServiceInfo().packageName; ContentValues values = new ContentValues(); values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName); @@ -1017,8 +1010,7 @@ public final class TvInputManagerService extends SystemService { @Override public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { - if (mContext.checkCallingPermission( - android.Manifest.permission.TV_INPUT_HARDWARE) + if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) != PackageManager.PERMISSION_GRANTED) { return null; } @@ -1032,10 +1024,25 @@ public final class TvInputManagerService extends SystemService { } @Override + public void registerTvInputInfo(TvInputInfo info, int deviceId) { + if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) + != PackageManager.PERMISSION_GRANTED) { + return; + } + + final long identity = Binder.clearCallingIdentity(); + try { + mTvInputHardwareManager.registerTvInputInfo(info, deviceId); + } 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) + ITvInputHardwareCallback callback, TvInputInfo info, int userId) + throws RemoteException { + if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) != PackageManager.PERMISSION_GRANTED) { return null; } @@ -1046,7 +1053,7 @@ public final class TvInputManagerService extends SystemService { userId, "acquireTvInputHardware"); try { return mTvInputHardwareManager.acquireHardware( - deviceId, callback, callingUid, resolvedUserId); + deviceId, callback, info, callingUid, resolvedUserId); } finally { Binder.restoreCallingIdentity(identity); } @@ -1055,8 +1062,7 @@ public final class TvInputManagerService extends SystemService { @Override public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) throws RemoteException { - if (mContext.checkCallingPermission( - android.Manifest.permission.TV_INPUT_HARDWARE) + if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) != PackageManager.PERMISSION_GRANTED) { return; } @@ -1099,16 +1105,16 @@ public final class TvInputManagerService extends SystemService { pw.println("UserState (" + userId + "):"); pw.increaseIndent(); - pw.println("inputMap: inputId -> TvInputInfo"); + pw.println("inputMap: inputId -> TvInputState"); pw.increaseIndent(); - for (TvInputInfo info : userState.inputMap.values()) { - pw.println(info.toString()); + for (TvInputState state : userState.inputMap.values()) { + pw.println(state.toString()); } pw.decreaseIndent(); - pw.println("packageList:"); + pw.println("packageSet:"); pw.increaseIndent(); - for (String packageName : userState.packageList) { + for (String packageName : userState.packageSet) { pw.println(packageName); } pw.decreaseIndent(); @@ -1169,7 +1175,6 @@ public final class TvInputManagerService extends SystemService { pw.println("mService: " + service.mService); pw.println("mCallback: " + service.mCallback); pw.println("mBound: " + service.mBound); - pw.println("mAvailable: " + service.mAvailable); pw.println("mReconnecting: " + service.mReconnecting); pw.decreaseIndent(); @@ -1196,18 +1201,38 @@ public final class TvInputManagerService extends SystemService { } pw.decreaseIndent(); + pw.println("callbackSet:"); + pw.increaseIndent(); + for (ITvInputManagerCallback callback : userState.callbackSet) { + pw.println(callback.toString()); + } + pw.decreaseIndent(); + pw.decreaseIndent(); } } } } + private static final class TvInputState { + // A TvInputInfo object which represents the TV input. + private TvInputInfo mInfo; + + // The state of TV input. Connected by default. + private int mState = INPUT_STATE_CONNECTED; + + @Override + public String toString() { + return "mInfo: " + mInfo + "; mState: " + mState; + } + } + private static final class UserState { - // A mapping from the TV input id to its TvInputInfo. - private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>(); + // A mapping from the TV input id to its TvInputState. + private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>(); - // A list of all TV input packages. - private final Set<String> packageList = new HashSet<String>(); + // A set of all TV input packages. + private final Set<String> packageSet = new HashSet<String>(); // A mapping from the token of a client to its state. private final Map<IBinder, ClientState> clientStateMap = @@ -1220,6 +1245,10 @@ public final class TvInputManagerService extends SystemService { // A mapping from the token of a TV input session to its state. private final Map<IBinder, SessionState> sessionStateMap = new HashMap<IBinder, SessionState>(); + + // A set of callbacks. + private final Set<ITvInputManagerCallback> callbackSet = + new HashSet<ITvInputManagerCallback>(); } private final class ClientState implements IBinder.DeathRecipient { @@ -1243,7 +1272,7 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { UserState userState = getUserStateLocked(mUserId); // DO NOT remove the client state of clientStateMap in this method. It will be - // removed in releaseSessionLocked() or unregisterCallbackInternalLocked(). + // removed in releaseSessionLocked() or unregisterClientInternalLocked(). ClientState clientState = userState.clientStateMap.get(mClientToken); if (clientState != null) { while (clientState.mSessionTokens.size() > 0) { @@ -1251,7 +1280,7 @@ public final class TvInputManagerService extends SystemService { clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId); } while (clientState.mInputIds.size() > 0) { - unregisterCallbackInternalLocked( + unregisterClientInternalLocked( mClientToken, clientState.mInputIds.get(0), mUserId); } } @@ -1269,7 +1298,6 @@ public final class TvInputManagerService extends SystemService { private ITvInputService mService; private ServiceCallback mCallback; private boolean mBound; - private boolean mAvailable; private boolean mReconnecting; private ServiceState(TvInputInfo inputInfo, int userId) { @@ -1325,16 +1353,18 @@ public final class TvInputManagerService extends SystemService { @Override public void onServiceConnected(ComponentName name, IBinder service) { + String inputId = mTvInputInfo.getId(); if (DEBUG) { - Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")"); + Slog.d(TAG, "onServiceConnected(inputId=" + inputId + ")"); } synchronized (mLock) { - ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId); + UserState userState = getUserStateLocked(mUserId); + ServiceState serviceState = userState.serviceStateMap.get(inputId); serviceState.mService = ITvInputService.Stub.asInterface(service); // Register a callback, if we need to. if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) { - serviceState.mCallback = new ServiceCallback(mUserId); + serviceState.mCallback = new ServiceCallback(mTvInputInfo.getId(), mUserId); try { serviceState.mService.registerCallback(serviceState.mCallback); } catch (RemoteException e) { @@ -1346,6 +1376,12 @@ public final class TvInputManagerService extends SystemService { for (IBinder sessionToken : serviceState.mSessionTokens) { createSessionInternalLocked(serviceState.mService, sessionToken, mUserId); } + + TvInputState inputState = userState.inputMap.get(inputId); + if (inputState != null && inputState.mState != INPUT_STATE_DISCONNECTED) { + notifyStateChangedLocked(userState, mTvInputInfo.getId(), + inputState.mState, null); + } } } @@ -1377,10 +1413,8 @@ public final class TvInputManagerService extends SystemService { } } - if (serviceState.mAvailable) { - serviceState.mAvailable = false; - broadcastServiceAvailabilityChangedLocked(serviceState); - } + notifyStateChangedLocked(userState, mTvInputInfo.getId(), + INPUT_STATE_DISCONNECTED, null); updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId); } } @@ -1388,24 +1422,22 @@ public final class TvInputManagerService extends SystemService { } private final class ServiceCallback extends ITvInputServiceCallback.Stub { + private final String mInputId; private final int mUserId; - ServiceCallback(int userId) { + ServiceCallback(String inputId, int userId) { + mInputId = inputId; mUserId = userId; } @Override - public void onAvailabilityChanged(String inputId, boolean isAvailable) { + public void onInputStateChanged(int state) { if (DEBUG) { - Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable=" - + isAvailable + ")"); + Slog.d(TAG, "onInputStateChanged(inputId=" + mInputId + ", state=" + + state + ")"); } synchronized (mLock) { - ServiceState serviceState = getServiceStateLocked(inputId, mUserId); - if (serviceState.mAvailable != isAvailable) { - serviceState.mAvailable = isAvailable; - broadcastServiceAvailabilityChangedLocked(serviceState); - } + setStateLocked(mInputId, state, mUserId); } } } @@ -1553,4 +1585,12 @@ public final class TvInputManagerService extends SystemService { mContentResolver.update(uri, values, null, null); } } + + final class Client { + public void setState(String inputId, int state) { + synchronized (mLock) { + setStateLocked(inputId, state, mCurrentUserId); + } + } + } } |