FileDocCategorySizeDatePackage
TvInputHardwareManager.javaAPI DocAndroid 5.1 API49697Thu Mar 12 22:22:42 GMT 2015com.android.server.tv

TvInputHardwareManager

public class TvInputHardwareManager extends Object implements TvInputHal.Callback
A helper class for TvInputManagerService to handle TV input hardware. This class does a basic connection management and forwarding calls to TvInputHal which eventually calls to tv_input HAL module.
hide

Fields Summary
private static final String
TAG
private final android.content.Context
mContext
private final Listener
mListener
private final TvInputHal
mHal
private final android.util.SparseArray
mConnections
private final List
mHardwareList
private final List
mHdmiDeviceList
private final android.util.SparseArray
mHardwareInputIdMap
private final android.util.SparseArray
mHdmiInputIdMap
private final Map
mInputMap
private final android.media.AudioManager
mAudioManager
private android.hardware.hdmi.IHdmiControlService
mHdmiControlService
private final android.hardware.hdmi.IHdmiHotplugEventListener
mHdmiHotplugEventListener
private final android.hardware.hdmi.IHdmiDeviceEventListener
mHdmiDeviceEventListener
private final android.hardware.hdmi.IHdmiSystemAudioModeChangeListener
mHdmiSystemAudioModeChangeListener
private final android.content.BroadcastReceiver
mVolumeReceiver
private int
mCurrentIndex
private int
mCurrentMaxIndex
private final boolean
mUseMasterVolume
private final android.util.SparseBooleanArray
mHdmiStateMap
private final List
mPendingHdmiDeviceEvents
private final android.os.Handler
mHandler
private final Object
mLock
Constructors Summary
public TvInputHardwareManager(android.content.Context context, Listener listener)


         
        mContext = context;
        mListener = listener;
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mUseMasterVolume = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_useMasterVolume);
        mHal.init();
    
Methods Summary
public android.media.tv.ITvInputHardwareacquireHardware(int deviceId, android.media.tv.ITvInputHardwareCallback callback, android.media.tv.TvInputInfo info, int callingUid, int resolvedUserId)
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, the latest service will get the object and all the other hardware are released. The release is notified via ITvInputHardwareCallback.onReleased().

        if (callback == null) {
            throw new NullPointerException();
        }
        synchronized (mLock) {
            Connection connection = mConnections.get(deviceId);
            if (connection == null) {
                Slog.e(TAG, "Invalid deviceId : " + deviceId);
                return null;
            }
            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
                TvInputHardwareImpl hardware =
                        new TvInputHardwareImpl(connection.getHardwareInfoLocked());
                try {
                    callback.asBinder().linkToDeath(connection, 0);
                } catch (RemoteException e) {
                    hardware.release();
                    return null;
                }
                connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
            }
            return connection.getHardwareLocked();
        }
    
public voidaddHardwareTvInput(int deviceId, android.media.tv.TvInputInfo info)

        synchronized (mLock) {
            String oldInputId = mHardwareInputIdMap.get(deviceId);
            if (oldInputId != null) {
                Slog.w(TAG, "Trying to override previous registration: old = "
                        + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
                        + info + ":" + deviceId);
            }
            mHardwareInputIdMap.put(deviceId, info.getId());
            mInputMap.put(info.getId(), info);

            // Process pending state changes

            // For logical HDMI devices, they have information from HDMI CEC signals.
            for (int i = 0; i < mHdmiStateMap.size(); ++i) {
                TvInputHardwareInfo hardwareInfo =
                        findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
                if (hardwareInfo == null) {
                    continue;
                }
                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
                if (inputId != null && inputId.equals(info.getId())) {
                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
                            inputId).sendToTarget();
                    return;
                }
            }
            // For the rest of the devices, we can tell by the number of available streams.
            Connection connection = mConnections.get(deviceId);
            if (connection != null) {
                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
                        convertConnectedToState(connection.getConfigsLocked().length > 0), 0,
                        info.getId()).sendToTarget();
                return;
            }
        }
    
public voidaddHdmiTvInput(int id, android.media.tv.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 = indexOfEqualValue(mHardwareInputIdMap, parentId);
            if (parentIndex < 0) {
                throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
            }
            String oldInputId = mHdmiInputIdMap.get(id);
            if (oldInputId != null) {
                Slog.w(TAG, "Trying to override previous registration: old = "
                        + mInputMap.get(oldInputId) + ":" + id + ", new = "
                        + info + ":" + id);
            }
            mHdmiInputIdMap.put(id, info.getId());
            mInputMap.put(info.getId(), info);
        }
    
private voidbuildHardwareListLocked()

        mHardwareList.clear();
        for (int i = 0; i < mConnections.size(); ++i) {
            mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
        }
    
public booleancaptureFrame(java.lang.String inputId, android.view.Surface surface, android.media.tv.TvStreamConfig config, int callingUid, int resolvedUserId)
Take a snapshot of the given TV input into the provided Surface.

        synchronized (mLock) {
            int deviceId = findDeviceIdForInputIdLocked(inputId);
            if (deviceId < 0) {
                Slog.e(TAG, "Invalid inputId : " + inputId);
                return false;
            }
            Connection connection = mConnections.get(deviceId);
            final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
            if (hardwareImpl != null) {
                // Stop previous capture.
                Runnable runnable = connection.getOnFirstFrameCapturedLocked();
                if (runnable != null) {
                    runnable.run();
                    connection.setOnFirstFrameCapturedLocked(null);
                }

                boolean result = hardwareImpl.startCapture(surface, config);
                if (result) {
                    connection.setOnFirstFrameCapturedLocked(new Runnable() {
                        @Override
                        public void run() {
                            hardwareImpl.stopCapture(config);
                        }
                    });
                }
                return result;
            }
        }
        return false;
    
private booleancheckUidChangedLocked(com.android.server.tv.TvInputHardwareManager$Connection connection, int callingUid, int resolvedUserId)

        Integer connectionCallingUid = connection.getCallingUidLocked();
        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
        if (connectionCallingUid == null || connectionResolvedUserId == null) {
            return true;
        }
        if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
            return true;
        }
        return false;
    
private intconvertConnectedToState(boolean connected)

        if (connected) {
            return INPUT_STATE_CONNECTED;
        } else {
            return INPUT_STATE_DISCONNECTED;
        }
    
private intfindDeviceIdForInputIdLocked(java.lang.String inputId)

        for (int i = 0; i < mConnections.size(); ++i) {
            Connection connection = mConnections.get(i);
            if (connection.getInfoLocked().getId().equals(inputId)) {
                return i;
            }
        }
        return -1;
    
private android.media.tv.TvInputHardwareInfofindHardwareInfoForHdmiPortLocked(int port)

        for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
            if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
                    && hardwareInfo.getHdmiPortId() == port) {
                return hardwareInfo;
            }
        }
        return null;
    
public java.util.ListgetAvailableTvStreamConfigList(java.lang.String inputId, int callingUid, int resolvedUserId)
Get the list of TvStreamConfig which is buffered mode.

        List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
        synchronized (mLock) {
            int deviceId = findDeviceIdForInputIdLocked(inputId);
            if (deviceId < 0) {
                Slog.e(TAG, "Invalid inputId : " + inputId);
                return configsList;
            }
            Connection connection = mConnections.get(deviceId);
            for (TvStreamConfig config : connection.getConfigsLocked()) {
                if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
                    configsList.add(config);
                }
            }
        }
        return configsList;
    
public java.util.ListgetHardwareList()

        synchronized (mLock) {
            return Collections.unmodifiableList(mHardwareList);
        }
    
public java.util.ListgetHdmiDeviceList()

        synchronized (mLock) {
            return Collections.unmodifiableList(mHdmiDeviceList);
        }
    
private floatgetMediaStreamVolume()

        return mUseMasterVolume ? 1.0f : ((float) mCurrentIndex / (float) mCurrentMaxIndex);
    
private voidhandleVolumeChange(android.content.Context context, android.content.Intent intent)

        String action = intent.getAction();
        if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
            int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
            if (streamType != AudioManager.STREAM_MUSIC) {
                return;
            }
            int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
            if (index == mCurrentIndex) {
                return;
            }
            mCurrentIndex = index;
        } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
            int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
            if (streamType != AudioManager.STREAM_MUSIC) {
                return;
            }
            // volume index will be updated at onMediaStreamVolumeChanged() through updateVolume().
        } else {
            Slog.w(TAG, "Unrecognized intent: " + intent);
            return;
        }
        synchronized (mLock) {
            for (int i = 0; i < mConnections.size(); ++i) {
                TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked();
                if (hardwareImpl != null) {
                    hardwareImpl.onMediaStreamVolumeChanged();
                }
            }
        }
    
private static intindexOfEqualValue(android.util.SparseArray map, T value)

        for (int i = 0; i < map.size(); ++i) {
            if (map.valueAt(i).equals(value)) {
                return i;
            }
        }
        return -1;
    
private static booleanintArrayContains(int[] array, int value)

        for (int element : array) {
            if (element == value) return true;
        }
        return false;
    
public voidonBootPhase(int phase)

        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
                    Context.HDMI_CONTROL_SERVICE));
            if (mHdmiControlService != null) {
                try {
                    mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
                    mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
                    mHdmiControlService.addSystemAudioModeChangeListener(
                            mHdmiSystemAudioModeChangeListener);
                    mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
                } catch (RemoteException e) {
                    Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
                }
            } else {
                Slog.w(TAG, "HdmiControlService is not available");
            }
            if (!mUseMasterVolume) {
                final IntentFilter filter = new IntentFilter();
                filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
                filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
                mContext.registerReceiver(mVolumeReceiver, filter);
            }
            updateVolume();
        }
    
public voidonDeviceAvailable(android.media.tv.TvInputHardwareInfo info, android.media.tv.TvStreamConfig[] configs)

        synchronized (mLock) {
            Connection connection = new Connection(info);
            connection.updateConfigsLocked(configs);
            mConnections.put(info.getDeviceId(), connection);
            buildHardwareListLocked();
            mHandler.obtainMessage(
                    ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
                processPendingHdmiDeviceEventsLocked();
            }
        }
    
public voidonDeviceUnavailable(int deviceId)

        synchronized (mLock) {
            Connection connection = mConnections.get(deviceId);
            if (connection == null) {
                Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
                return;
            }
            connection.resetLocked(null, null, null, null, null);
            mConnections.remove(deviceId);
            buildHardwareListLocked();
            TvInputHardwareInfo info = connection.getHardwareInfoLocked();
            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
                // Remove HDMI devices linked with this hardware.
                for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
                    HdmiDeviceInfo deviceInfo = it.next();
                    if (deviceInfo.getPortId() == info.getHdmiPortId()) {
                        mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
                                deviceInfo).sendToTarget();
                        it.remove();
                    }
                }
            }
            mHandler.obtainMessage(
                    ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
        }
    
public voidonFirstFrameCaptured(int deviceId, int streamId)

        synchronized (mLock) {
            Connection connection = mConnections.get(deviceId);
            if (connection == null) {
                Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
                        + deviceId);
                return;
            }
            Runnable runnable = connection.getOnFirstFrameCapturedLocked();
            if (runnable != null) {
                runnable.run();
                connection.setOnFirstFrameCapturedLocked(null);
            }
        }
    
public voidonStreamConfigurationChanged(int deviceId, android.media.tv.TvStreamConfig[] configs)

        synchronized (mLock) {
            Connection connection = mConnections.get(deviceId);
            if (connection == null) {
                Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
                        + deviceId);
                return;
            }
            connection.updateConfigsLocked(configs);
            String inputId = mHardwareInputIdMap.get(deviceId);
            if (inputId != null) {
                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
                        convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget();
            }
            ITvInputHardwareCallback callback = connection.getCallbackLocked();
            if (callback != null) {
                try {
                    callback.onStreamConfigChanged(configs);
                } catch (RemoteException e) {
                    Slog.e(TAG, "error in onStreamConfigurationChanged", e);
                }
            }
        }
    
private voidprocessPendingHdmiDeviceEventsLocked()

        for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
            Message msg = it.next();
            HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
            TvInputHardwareInfo hardwareInfo =
                    findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
            if (hardwareInfo != null) {
                msg.sendToTarget();
                it.remove();
            }
        }
    
public voidreleaseHardware(int deviceId, android.media.tv.ITvInputHardware hardware, int callingUid, int resolvedUserId)
Release the specified hardware.

        synchronized (mLock) {
            Connection connection = mConnections.get(deviceId);
            if (connection == null) {
                Slog.e(TAG, "Invalid deviceId : " + deviceId);
                return;
            }
            if (connection.getHardwareLocked() != hardware
                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
                return;
            }
            connection.resetLocked(null, null, null, null, null);
        }
    
public voidremoveTvInput(java.lang.String inputId)

        synchronized (mLock) {
            mInputMap.remove(inputId);
            int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
            if (hardwareIndex >= 0) {
                mHardwareInputIdMap.removeAt(hardwareIndex);
            }
            int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
            if (deviceIndex >= 0) {
                mHdmiInputIdMap.removeAt(deviceIndex);
            }
        }
    
private voidupdateVolume()

        mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);