From 4f9f57cede3de2e2aa3045e04b485b176ab22dbd Mon Sep 17 00:00:00 2001 From: Ji-Hwan Lee Date: Sat, 19 Jul 2014 22:20:31 +0900 Subject: TIF: Extend multiple TV input per service for HDMI logical devices Remove ITvInputManager.registerTvInputInfo() and let addTvInputInfo*() cover the registration. Bug: 15570939 Change-Id: Ic36701de96696e7fe32fc1faa0d5f6fde53f6666 --- .../android/server/tv/TvInputHardwareManager.java | 127 +++++++++++++++------ .../android/server/tv/TvInputManagerService.java | 122 ++++++++++++-------- 2 files changed, 167 insertions(+), 82 deletions(-) (limited to 'services') diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 332e426..0a9c646 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -42,6 +42,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.ArrayMap; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -51,8 +52,10 @@ import android.view.Surface; import com.android.server.SystemService; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -65,20 +68,27 @@ import java.util.Set; */ class TvInputHardwareManager implements TvInputHal.Callback { private static final String TAG = TvInputHardwareManager.class.getSimpleName(); + + private final Context mContext; + private final Listener mListener; private final TvInputHal mHal = new TvInputHal(this); private final SparseArray mConnections = new SparseArray(); private final List mInfoList = new ArrayList(); - private final Context mContext; - private final Listener mListener; - private final Set mActiveHdmiSources = new HashSet(); + /* A map from a device ID to the matching TV input ID. */ + private final SparseArray mHardwareInputIdMap = new SparseArray(); + /* A map from a HDMI logical address to the matching TV input ID. */ + private final SparseArray mHdmiCecInputIdMap = new SparseArray(); + private final Map mInputMap = new ArrayMap(); + private final AudioManager mAudioManager; - private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray(); - // TODO: Should handle INACTIVE case. - private final SparseArray mTvInputInfoMap = new SparseArray(); - private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener(); + private IHdmiControlService mHdmiControlService; private final IHdmiHotplugEventListener mHdmiHotplugEventListener = new HdmiHotplugEventListener(); + private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener(); private final IHdmiInputChangeListener mHdmiInputChangeListener = new HdmiInputChangeListener(); + private final Set mActiveHdmiSources = new HashSet(); + // TODO: Should handle INACTIVE case. + private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray(); // Calls to mListener should happen here. private final Handler mHandler = new ListenerHandler(); @@ -94,23 +104,22 @@ class TvInputHardwareManager implements TvInputHal.Callback { public void onBootPhase(int phase) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface( - ServiceManager.getService(Context.HDMI_CONTROL_SERVICE)); - if (hdmiControlService != null) { + mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService( + Context.HDMI_CONTROL_SERVICE)); + if (mHdmiControlService != null) { try { - hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener); - hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener); - hdmiControlService.setInputChangeListener(mHdmiInputChangeListener); + mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener); + mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener); + mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener); } catch (RemoteException e) { - Slog.w(TAG, "Error registering listeners to HdmiControlService:" + e); + Slog.w(TAG, "Error registering listeners to HdmiControlService:", e); } } } } @Override - public void onDeviceAvailable( - TvInputHardwareInfo info, TvStreamConfig[] configs) { + public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) { synchronized (mLock) { Connection connection = new Connection(info); connection.updateConfigsLocked(configs); @@ -139,8 +148,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { connection.resetLocked(null, null, null, null, null); mConnections.remove(deviceId); buildInfoListLocked(); + TvInputHardwareInfo info = connection.getHardwareInfoLocked(); mHandler.obtainMessage( - ListenerHandler.HARDWARE_DEVICE_REMOVED, deviceId, 0).sendToTarget(); + ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget(); } } @@ -157,7 +167,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { try { connection.getCallbackLocked().onStreamConfigChanged(configs); } catch (RemoteException e) { - Slog.e(TAG, "onStreamConfigurationChanged: " + e); + Slog.e(TAG, "error in onStreamConfigurationChanged", e); } } } @@ -168,6 +178,17 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + public List getHdmiCecInputDeviceList() { + if (mHdmiControlService != null) { + try { + return mHdmiControlService.getInputDevices(); + } catch (RemoteException e) { + Slog.e(TAG, "error in getHdmiCecInputDeviceList", e); + } + } + return Collections.emptyList(); + } + private boolean checkUidChangedLocked( Connection connection, int callingUid, int resolvedUserId) { Integer connectionCallingUid = connection.getCallingUidLocked(); @@ -189,17 +210,19 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } - public void registerTvInputInfo(TvInputInfo info, int deviceId) { + public void addHardwareTvInput(int deviceId, TvInputInfo info) { if (info.getType() == TvInputInfo.TYPE_VIRTUAL) { throw new IllegalArgumentException("info (" + info + ") has virtual type."); } synchronized (mLock) { - if (mTvInputInfoMap.indexOfKey(deviceId) >= 0) { + String oldInputId = mHardwareInputIdMap.get(deviceId); + if (oldInputId != null) { Slog.w(TAG, "Trying to override previous registration: old = " - + mTvInputInfoMap.get(deviceId) + ":" + deviceId + ", new = " + + mInputMap.get(oldInputId) + ":" + deviceId + ", new = " + info + ":" + deviceId); } - mTvInputInfoMap.put(deviceId, info); + mHardwareInputIdMap.put(deviceId, info.getId()); + mInputMap.put(info.getId(), info); for (int i = 0; i < mHdmiStateMap.size(); ++i) { String inputId = findInputIdForHdmiPortLocked(mHdmiStateMap.keyAt(i)); @@ -212,6 +235,41 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + public void addHdmiCecTvInput(int logicalAddress, TvInputInfo info) { + if (info.getType() != TvInputInfo.TYPE_HDMI) { + throw new IllegalArgumentException("info (" + info + ") has non-HDMI type."); + } + synchronized (mLock) { + String parentId = info.getParentId(); + int parentIndex = mHardwareInputIdMap.indexOfValue(parentId); + if (parentIndex < 0 || !parentId.equals(mHardwareInputIdMap.valueAt(parentIndex))) { + throw new IllegalArgumentException("info (" + info + ") has invalid parentId."); + } + String oldInputId = mHdmiCecInputIdMap.get(logicalAddress); + if (oldInputId != null) { + Slog.w(TAG, "Trying to override previous registration: old = " + + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = " + + info + ":" + logicalAddress); + } + mHdmiCecInputIdMap.put(logicalAddress, info.getId()); + mInputMap.put(info.getId(), info); + } + } + + public void removeTvInput(String inputId) { + synchronized (mLock) { + mInputMap.remove(inputId); + int hardwareIndex = mHardwareInputIdMap.indexOfValue(inputId); + if (hardwareIndex >= 0) { + mHardwareInputIdMap.removeAt(hardwareIndex); + } + int cecIndex = mHdmiCecInputIdMap.indexOfValue(inputId); + if (cecIndex >= 0) { + mHdmiCecInputIdMap.removeAt(cecIndex); + } + } + } + /** * 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, @@ -267,8 +325,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { 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 mHardwareInputIdMap.get(hardwareInfo.getDeviceId()); } } return null; @@ -295,7 +352,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { try { mCallback.onReleased(); } catch (RemoteException e) { - Slog.e(TAG, "Connection::resetHardware: " + e); + Slog.e(TAG, "error in Connection::resetLocked", e); } mHardware.release(); } @@ -309,7 +366,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { try { mCallback.onStreamConfigChanged(getConfigsLocked()); } catch (RemoteException e) { - Slog.e(TAG, "Connection::resetHardware: " + e); + Slog.e(TAG, "error in Connection::resetLocked", e); } } } @@ -501,7 +558,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { interface Listener { public void onStateChanged(String inputId, int state); public void onHardwareDeviceAdded(TvInputHardwareInfo info); - public void onHardwareDeviceRemoved(int deviceId); + public void onHardwareDeviceRemoved(TvInputHardwareInfo info); public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice); public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice); } @@ -510,8 +567,8 @@ class TvInputHardwareManager implements TvInputHal.Callback { private static final int STATE_CHANGED = 1; private static final int HARDWARE_DEVICE_ADDED = 2; private static final int HARDWARE_DEVICE_REMOVED = 3; - private static final int CEC_DEVICE_ADDED = 4; - private static final int CEC_DEVICE_REMOVED = 5; + private static final int HDMI_CEC_DEVICE_ADDED = 4; + private static final int HDMI_CEC_DEVICE_REMOVED = 5; @Override public final void handleMessage(Message msg) { @@ -528,16 +585,16 @@ class TvInputHardwareManager implements TvInputHal.Callback { break; } case HARDWARE_DEVICE_REMOVED: { - int deviceId = msg.arg1; - mListener.onHardwareDeviceRemoved(deviceId); + TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj; + mListener.onHardwareDeviceRemoved(info); break; } - case CEC_DEVICE_ADDED: { + case HDMI_CEC_DEVICE_ADDED: { HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj; mListener.onHdmiCecDeviceAdded(info); break; } - case CEC_DEVICE_REMOVED: { + case HDMI_CEC_DEVICE_REMOVED: { HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj; mListener.onHdmiCecDeviceRemoved(info); break; @@ -571,8 +628,8 @@ class TvInputHardwareManager implements TvInputHal.Callback { @Override public void onStatusChanged(HdmiCecDeviceInfo deviceInfo, boolean activated) { mHandler.obtainMessage( - activated ? ListenerHandler.CEC_DEVICE_ADDED - : ListenerHandler.CEC_DEVICE_REMOVED, + activated ? ListenerHandler.HDMI_CEC_DEVICE_ADDED + : ListenerHandler.HDMI_CEC_DEVICE_REMOVED, 0, 0, deviceInfo).sendToTarget(); } } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index c112b13..01aaca0 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -220,7 +220,7 @@ public final class TvInputManagerService extends SystemService { List services = pm.queryIntentServices( new Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); - List infoList = new ArrayList(); + List inputList = new ArrayList(); for (ResolveInfo ri : services) { ServiceInfo si = ri.serviceInfo; if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { @@ -229,7 +229,7 @@ public final class TvInputManagerService extends SystemService { continue; } try { - infoList.clear(); + inputList.clear(); ComponentName service = new ComponentName(si.packageName, si.name); if (hasHardwarePermission(pm, service)) { ServiceState serviceState = userState.serviceStateMap.get(service); @@ -240,13 +240,13 @@ public final class TvInputManagerService extends SystemService { serviceState = new ServiceState(service, userId); userState.serviceStateMap.put(service, serviceState); } else { - infoList.addAll(serviceState.mInputList); + inputList.addAll(serviceState.mInputList); } } else { - infoList.add(TvInputInfo.createTvInputInfo(mContext, ri)); + inputList.add(TvInputInfo.createTvInputInfo(mContext, ri)); } - for (TvInputInfo info : infoList) { + for (TvInputInfo info : inputList) { if (DEBUG) Slog.d(TAG, "add " + info.getId()); TvInputState state = userState.inputMap.get(info.getId()); if (state == null) { @@ -1213,22 +1213,6 @@ public final class TvInputManagerService extends SystemService { } @Override - public void registerTvInputInfo(TvInputInfo info, int deviceId) { - // TODO: Revisit to sort out deviceId ownership. - 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, TvInputInfo info, int userId) throws RemoteException { @@ -1550,8 +1534,6 @@ public final class TvInputManagerService extends SystemService { Slog.d(TAG, "onServiceConnected(name=" + name + ")"); } synchronized (mLock) { - List hardwareInfoList = - mTvInputHardwareManager.getHardwareList(); UserState userState = getUserStateLocked(mUserId); ServiceState serviceState = userState.serviceStateMap.get(mName); serviceState.mService = ITvInputService.Stub.asInterface(service); @@ -1580,6 +1562,8 @@ public final class TvInputManagerService extends SystemService { } if (serviceState.mIsHardware) { + List hardwareInfoList = + mTvInputHardwareManager.getHardwareList(); for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) { try { serviceState.mService.notifyHardwareAdded(hardwareInfo); @@ -1588,7 +1572,15 @@ public final class TvInputManagerService extends SystemService { } } - // TODO: Grab CEC devices and notify them to the service. + List cecDeviceInfoList = + mTvInputHardwareManager.getHdmiCecInputDeviceList(); + for (HdmiCecDeviceInfo cecDeviceInfo : cecDeviceInfoList) { + try { + serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo); + } catch (RemoteException e) { + Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e); + } + } } } } @@ -1642,32 +1634,49 @@ public final class TvInputManagerService extends SystemService { mUserId = userId; } + private void ensureHardwarePermission() { + if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("The caller does not have hardware permission"); + } + } + + private void ensureValidInput(TvInputInfo inputInfo) { + if (inputInfo.getId() == null || !mName.equals(inputInfo.getComponent())) { + throw new IllegalArgumentException("Invalid TvInputInfo"); + } + } + + private void addTvInputLocked(TvInputInfo inputInfo) { + ServiceState serviceState = getServiceStateLocked(mName, mUserId); + serviceState.mInputList.add(inputInfo); + buildTvInputListLocked(mUserId); + } + @Override - public void addTvInput(TvInputInfo inputInfo) { + public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) { + ensureHardwarePermission(); + ensureValidInput(inputInfo); synchronized (mLock) { - if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) - != PackageManager.PERMISSION_GRANTED) { - Slog.w(TAG, "The caller does not have permission to add a TV input (" - + inputInfo + ")."); - return; - } + mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo); + addTvInputLocked(inputInfo); + } + } - ServiceState serviceState = getServiceStateLocked(mName, mUserId); - serviceState.mInputList.add(inputInfo); - buildTvInputListLocked(mUserId); + @Override + public void addHdmiCecTvInput(int logicalAddress, TvInputInfo inputInfo) { + ensureHardwarePermission(); + ensureValidInput(inputInfo); + synchronized (mLock) { + mTvInputHardwareManager.addHdmiCecTvInput(logicalAddress, inputInfo); + addTvInputLocked(inputInfo); } } @Override public void removeTvInput(String inputId) { + ensureHardwarePermission(); synchronized (mLock) { - if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) - != PackageManager.PERMISSION_GRANTED) { - Slog.w(TAG, "The caller does not have permission to remove a TV input (" - + inputId + ")."); - return; - } - ServiceState serviceState = getServiceStateLocked(mName, mUserId); boolean removed = false; for (Iterator it = serviceState.mInputList.iterator(); @@ -1680,6 +1689,7 @@ public final class TvInputManagerService extends SystemService { } if (removed) { buildTvInputListLocked(mUserId); + mTvInputHardwareManager.removeTvInput(inputId); } else { Slog.e(TAG, "TvInputInfo with inputId=" + inputId + " not found."); } @@ -1856,14 +1866,14 @@ public final class TvInputManagerService extends SystemService { } @Override - public void onHardwareDeviceRemoved(int deviceId) { + public void onHardwareDeviceRemoved(TvInputHardwareInfo info) { synchronized (mLock) { UserState userState = getUserStateLocked(mCurrentUserId); // Broadcast the event to all hardware inputs. for (ServiceState serviceState : userState.serviceStateMap.values()) { if (!serviceState.mIsHardware || serviceState.mService == null) continue; try { - serviceState.mService.notifyHardwareRemoved(deviceId); + serviceState.mService.notifyHardwareRemoved(info); } catch (RemoteException e) { Slog.e(TAG, "error in notifyHardwareRemoved", e); } @@ -1872,16 +1882,34 @@ public final class TvInputManagerService extends SystemService { } @Override - public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice) { + public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDeviceInfo) { synchronized (mLock) { - // TODO + UserState userState = getUserStateLocked(mCurrentUserId); + // Broadcast the event to all hardware inputs. + for (ServiceState serviceState : userState.serviceStateMap.values()) { + if (!serviceState.mIsHardware || serviceState.mService == null) continue; + try { + serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo); + } catch (RemoteException e) { + Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e); + } + } } } @Override - public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice) { + public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDeviceInfo) { synchronized (mLock) { - // TODO + UserState userState = getUserStateLocked(mCurrentUserId); + // Broadcast the event to all hardware inputs. + for (ServiceState serviceState : userState.serviceStateMap.values()) { + if (!serviceState.mIsHardware || serviceState.mService == null) continue; + try { + serviceState.mService.notifyHdmiCecDeviceRemoved(cecDeviceInfo); + } catch (RemoteException e) { + Slog.e(TAG, "error in notifyHdmiCecDeviceRemoved", e); + } + } } } } -- cgit v1.1