TIF: Revisit availability changes
Bug: 15838097, Bug: 15973274 Change-Id: Ida060696cb6222c8ced576d86c100c25d94dc5c0
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
25
media/java/android/media/tv/ITvInputManagerCallback.aidl
Normal file
25
media/java/android/media/tv/ITvInputManagerCallback.aidl
Normal file
@@ -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);
|
||||
}
|
||||
@@ -24,5 +24,5 @@ import android.content.ComponentName;
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITvInputServiceCallback {
|
||||
void onAvailabilityChanged(in String inputId, boolean isAvailable);
|
||||
void onInputStateChanged(int state);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,14 +637,43 @@ public final class TvInputManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
|
||||
for (IBinder clientToken : serviceState.mClientTokens) {
|
||||
try {
|
||||
ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged(
|
||||
serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "error in onAvailabilityChanged", e);
|
||||
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 {
|
||||
targetCallback.onInputStateChanged(inputId, state);
|
||||
} catch (RemoteException 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1031,11 +1023,26 @@ 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 UserState {
|
||||
// A mapping from the TV input id to its TvInputInfo.
|
||||
private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
|
||||
private static final class TvInputState {
|
||||
// A TvInputInfo object which represents the TV input.
|
||||
private TvInputInfo mInfo;
|
||||
|
||||
// A list of all TV input packages.
|
||||
private final Set<String> packageList = new HashSet<String>();
|
||||
// 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 TvInputState.
|
||||
private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user