Android TV Input Framework(TIF)--2 构建TV input list

TvInputManagerService管理着系统的各种输入,TV Input主要分为三种类型:

  • hardware input:主要包含TV内建的各种输入端口,比如tuner、component, composite, hdmi。

  • 非hardware input: 视频点播等非内建的硬件端口属于这种类型。

  • HDMI logic input:带有HDMI CEC的设备属于这种类型。


TvInputManagerService由SystemServer创建,我们先看看它的构造方法

  public TvInputManagerService(Context context) {
        super(context);
        ...
        mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());

        synchronized (mLock) {
            mUserStates.put(mCurrentUserId, new UserState(mContext, mCurrentUserId));
        }
    }

在构造方法中会创建一下TvInputHardwareManager实例,并传入一个HardwareListener实例给TvInputHardwareManager。TvInputHardwareManager通过TvInputHal来获取TV硬件输入的各种状态,并通过HardwareListener通知TvInputManagerService。

class TvInputHardwareManager implements TvInputHal.Callback

public interface Callback {
        public void onDeviceAvailable(
                TvInputHardwareInfo info, TvStreamConfig[] configs);
        public void onDeviceUnavailable(int deviceId);
        public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
        public void onFirstFrameCaptured(int deviceId, int streamId);
    }

 public TvInputHardwareManager(Context context, Listener listener) {
        mContext = context;
        mListener = listener;
        ...
        mHal.init();
    }

TvInputHardwareManager实现了TvInputHal.Callback接口,在构造方法中调用mHal.init()对TvInputHal进行初始化,在TvInputHal初始化过程中,所有TV内建的Input都会通过onDeviceAvailable通知给TvInputHardWareManager,我们看一下onDeviceAvailable的实现:

  public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
        synchronized (mLock) {
            ...
            buildHardwareListLocked();
            mHandler.obtainMessage(
                    ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
            ...
            }
        }
    }

onDeviceAvailable会调用buildHardwareListLocked把Tv Input的信息放入一个链表,TV Input的信息用TvInputHardwareInfo类表示,

private void buildHardwareListLocked() {
        mHardwareList.clear();
        for (int i = 0; i < mConnections.size(); ++i) {
            mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
        }
    }

然后通过mHandler.obtainMessage(ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget()发送到handler线程处理,

 public final void handleMessage(Message msg) {
            switch (msg.what) {
                ...
                case HARDWARE_DEVICE_ADDED: {
                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
                    mListener.onHardwareDeviceAdded(info);
                    break;
                }
                ...

还记得TvInputManagerService在创建TvInputHardwareManager的时候传入的HardwareListener吗?它就是mListener, HardwareListener的onHardwareDeviceAdded会被调用,

public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
            synchronized (mLock) {
                UserState userState = getUserStateLocked(mCurrentUserId);
                // Broadcast the event to all hardware inputs.
                for (ServiceState serviceState : userState.serviceStateMap.values()) {
                    if (!serviceState.isHardware || serviceState.service == null) continue;
                    try {
                        serviceState.service.notifyHardwareAdded(info);
                    } catch (RemoteException e) {
                        Slog.e(TAG, "error in notifyHardwareAdded", e);
                    }
                }
            }
        }

到目前为止,我们只是对TvInputManagerService的构造方法进行分析,userState.serviceStateMap还是空的,所以这个时候onHardwareDeviceAdded被调用其实什么事情都没有做。


我们接着分析TvInputManagerService的初始化过程,

 public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            registerBroadcastReceivers();
        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
            synchronized (mLock) {
                buildTvInputListLocked(mCurrentUserId, null);
                buildTvContentRatingSystemListLocked(mCurrentUserId);
            }
        }
        mTvInputHardwareManager.onBootPhase(phase);
    }

当第三方的app可以启动的时候,即phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START,调用buildTvInputListLocked开始构建Tv Input List。

    private void buildTvInputListLocked(int userId, String[] updatedPackages) {
        UserState userState = getUserStateLocked(userId);
        userState.packageSet.clear();

        if (DEBUG) Slog.d(TAG, "buildTvInputList");
        PackageManager pm = mContext.getPackageManager();
        List services = pm.queryIntentServices(
                new Intent(TvInputService.SERVICE_INTERFACE),
                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);                       ---<1>
        List inputList = new ArrayList();
        for (ResolveInfo ri : services) {
            ServiceInfo si = ri.serviceInfo;
            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
                        + android.Manifest.permission.BIND_TV_INPUT);
                continue;
            }
            ComponentName component = new ComponentName(si.packageName, si.name);
            if (hasHardwarePermission(pm, component)) {
                ServiceState serviceState = userState.serviceStateMap.get(component);
                if (serviceState == null) {
                    // We see this hardware TV input service for the first time; we need to
                    // prepare the ServiceState object so that we can connect to the service and
                    // let it add TvInputInfo objects to mInputList if there's any.
                    serviceState = new ServiceState(component, userId);                     ---<2>
                    userState.serviceStateMap.put(component, serviceState);
                    updateServiceConnectionLocked(component, userId);    ---<3>
                } else {
                    inputList.addAll(serviceState.inputList);            ---<4>
                }
            } else {
                try {
                    inputList.add(TvInputInfo.createTvInputInfo(mContext, ri));---<5>
                } catch (XmlPullParserException | IOException e) {
                    Slog.e(TAG, "failed to load TV input " + si.name, e);
                    continue;
                }
            }
            userState.packageSet.add(si.packageName);
        }

        Map inputMap = new HashMap();             ---<6>
        for (TvInputInfo info : inputList) {
            if (DEBUG) {
                Slog.d(TAG, "add " + info.getId());
            }
            TvInputState state = userState.inputMap.get(info.getId());
            if (state == null) {
                state = new TvInputState();     
            }
            state.info = info;
            inputMap.put(info.getId(), state);       
        }

        for (String inputId : inputMap.keySet()) {
            if (!userState.inputMap.containsKey(inputId)) {              ---<7>
                notifyInputAddedLocked(userState, inputId);
            } else if (updatedPackages != null) {
                // Notify the package updates
                ComponentName component = inputMap.get(inputId).info.getComponent();
                for (String updatedPackage : updatedPackages) {
                    if (component.getPackageName().equals(updatedPackage)) {
                        updateServiceConnectionLocked(component, userId);
                        notifyInputUpdatedLocked(userState, inputId);
                        break;
                    }
                }
            }
        }

        for (String inputId : userState.inputMap.keySet()) {
            if (!inputMap.containsKey(inputId)) {    ---<8>
                TvInputInfo info = userState.inputMap.get(inputId).info;
                ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
                if (serviceState != null) {
                    abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
                }
                notifyInputRemovedLocked(userState, inputId);
            }
        }

        userState.inputMap.clear();
        userState.inputMap = inputMap;               ---<9>
    }

这个方法比较长,关键行标上了序号,下面逐一解释:

  1. 查找所有的package中的service,如果package的service在AndroidManifest.xml中声明了如下属性:

    android:permission="android.permission.BIND_TV_INPUT"

    那么就认为这个service代表一种Tv Input,这些service都继承自TvInputService。对于TV内建的输入端口,还要在package的AndroidManifest.xml声明

    <uses-permission android:name="android.permission.TV_INPUT_HARDWARE" />

  2. 对于内建的硬件输入端口,TvInputManangerService会创建对应的ServiceState实例,并跟对应的Service建立连接,

  private ServiceState(ComponentName component, int userId) {
            this.component = component;
            this.connection = new InputServiceConnection(component, userId);
            this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
        }

注意this.connection = new InputServiceConnection(component, userId),稍后会用到。

 private void updateServiceConnectionLocked(ComponentName component, int userId) {
     ...
            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
            serviceState.bound = mContext.bindServiceAsUser(
                    i, serviceState.connection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
       ...
    }

3.在updateServiceConnectionLocked中通过Intent跟service建立连接,这个时候inputList是空的,buildTvInputListLocked继续执行返回。当跟service连接成功以后,serviceState.connection.onServiceConnected会被回调,就是前面说的InputServiceConnection.onServiceConnected。

public void onServiceConnected(ComponentName component, IBinder service) {
          ...
                // Register a callback, if we need to.
                if (serviceState.isHardware && serviceState.callback == null) {
                    serviceState.callback = new ServiceCallback(mComponent, mUserId);
                    try {
                        serviceState.service.registerCallback(serviceState.callback);
                    } catch (RemoteException e) {
                        Slog.e(TAG, "error in registerCallback", e);
                    }
                }
               ...
                if (serviceState.isHardware) {
                    List hardwareInfoList =
                            mTvInputHardwareManager.getHardwareList();
                    for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
                        try {
                            serviceState.service.notifyHardwareAdded(hardwareInfo);
                        } catch (RemoteException e) {
                            Slog.e(TAG, "error in notifyHardwareAdded", e);
                        }
                    }

                    List deviceInfoList =
                            mTvInputHardwareManager.getHdmiDeviceList();
                    for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
                        try {
                            serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
                        } catch (RemoteException e) {
                            Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
                        }
                    }
                }
            }
        }

接着会向Service注册callback,后面会用到。然后通过mTvInputHardwareManager.getHardwareList获取之前初始化的时候保存的Hardware list,调用service的notifyHardwareAdded方法,我们看一下TvInputService的notifyHardwareAdded实现:

public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) {
                mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_TV_INPUT,
                        hardwareInfo).sendToTarget();
            }

通知Handler线程DO_ADD_HARDWARE_TV_INPUT,

public final void handleMessage(Message msg) {
            switch (msg.what) {
            ...
            case DO_ADD_HARDWARE_TV_INPUT: {
                    TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
                    TvInputInfo inputInfo = onHardwareAdded(hardwareInfo);
                    if (inputInfo != null) {
                        broadcastAddHardwareTvInput(hardwareInfo.getDeviceId(), inputInfo);
                    }
                    return;
                }
                ...

然后service的onHardwareAdded方法被调用,并返回TvInputInfo,这代表一个TV Input,然后通过broadcastAddHardwareTvInput通知TvInputManagerService,

 private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
            int n = mCallbacks.beginBroadcast();
            for (int i = 0; i < n; ++i) {
                try {
                    mCallbacks.getBroadcastItem(i).addHardwareTvInput(deviceId, inputInfo);
                } catch (RemoteException e) {
                    Log.e(TAG, "Error while broadcasting.", e);
                }
            }
            mCallbacks.finishBroadcast();
        }

我们在跟service建立连接以后,注册了callback,callback的addHardwareTvInput被调用,我们看一下TvInputManagerService中addHardwareTvInput的实现,

 private void addTvInputLocked(TvInputInfo inputInfo) {
            ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
            serviceState.inputList.add(inputInfo);
            buildTvInputListLocked(mUserId, null);
        }

        @Override
        public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
            ensureHardwarePermission();
            ensureValidInput(inputInfo);
            synchronized (mLock) {
                mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo);
                addTvInputLocked(inputInfo);
            }
        }

addHardwareTvInput会调用addTvInputLocked,addTvInputLocked把TvInputInfo存入serviceState.inputList,接着调用buildTvInputListLocked重新构建Tv input list。


4.再次调用buildTvInputListLocked的时候,serviceState不为null,把serviceState.inputList放入到inputList。
5. 如果不是TV内建的硬件Input,直接创建TvInputInfo并放入InputList。
6. 每一个Tv Input都对应一个TvInputState,通过inputId在inputMap中索引。
7. 如果新构建的inputMap中的inputId在userState.inputMap中没有,表明这个TV input是新增加的,如果TV app有注册callback,那么TV app的onInputAdded会被调用。
8. 如果userState.inputMap中的inputId在新构建的inputMap中没有,表明这个TV Input被移除,如果TV app有注册callback,那么TV app的onInputRemoved会被调用。
9. userState.inputMap被新构建的inputMap替换,至此TV input list构建完成。


下面用序列图来展示一下整个过程

Created with Raphaël 2.1.0 TvInputManagerService TvInputManagerService TvInputHardwareManager TvInputHardwareManager TvInputHal TvInputHal TvInputService TvInputService init deviceAvailableFromNative onDeviceAvaliable buildHardwareListLocked onHardwareDeviceAdded buildTvInputListLocked updateServiceConnectionLocked mContext.bindServiceAsUser onServiceConnected registerCallback getHardwareList notifyHardwareAdded onHardwareAdded broadcastAddHardwareTvInput addHardwareTvInput addTvInputLocked buildTvInputListLocked notifyHdmiDeviceAdded onHdmiDeviceAdded broadcastAddHdmiTvInput addHdmiTvInput addTvInputLocked buildTvInputListLocked

你可能感兴趣的:(Android开发)