日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

Android TV : 电视输入框架(TIF)剖析

發(fā)布時(shí)間:2024/4/24 综合教程 37 生活家
生活随笔 收集整理的這篇文章主要介紹了 Android TV : 电视输入框架(TIF)剖析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Android TIF(Android TV input Framework)是Google向電視制造商提供了一套標(biāo)準(zhǔn)的API,用于創(chuàng)建Input模塊來(lái)控制Android電視。這套API的底層實(shí)現(xiàn)的原理是aidl和provider,從而進(jìn)行了跨進(jìn)程通信。系統(tǒng)或第三方的應(yīng)用可以通過TIF獲得所有輸入(input)的信源(輸入的模塊包括:搜臺(tái)模塊,MDMI模塊,網(wǎng)絡(luò)模塊等),然后通過aidl切臺(tái)輸出到屏幕上。

1.電視相關(guān)的知識(shí):
    HDMI:高清晰度多媒體接口(英文:High Definition Multimedia Interface,HDMI)是一種數(shù)字化視頻/音頻接口技術(shù),是適合影像傳輸?shù)膶S眯蛿?shù)字化接口。
    IPTV:網(wǎng)絡(luò)電視,也叫VOD電視,三方比如說(shuō)某某視頻公司提供的視頻資源在電視上播放。
    DTV:數(shù)字電視
    ATV:模擬電視
2.TIF的組成部分:
    1)TV Provider (com.android.providers.tv.TvProvider):一個(gè)包含頻道、節(jié)目和相關(guān)權(quán)限的數(shù)據(jù)庫(kù)。
    2)TV App (com.android.tv.TvActivity):一個(gè)和用戶交互的系統(tǒng)應(yīng)用。
    3)TV Input Manager (android.media.tv.TvInputManager):一個(gè)中間接口層,能夠讓TV Inputs和TV App進(jìn)行通訊。
    4)TV Input:可以看做是一個(gè)代表物理或者虛擬的電視接收器或者輸入端口的應(yīng)用。Input在TIF中可以看做是一個(gè)輸入源。
    5)TV Input HAL (tv_input module):TV Input的硬件抽象層,可以讓系統(tǒng)的TV inputs訪問TV特有硬件。
    6)Parental Control:兒童鎖,一種可以鎖住某些頻道和節(jié)目的技術(shù)。
    7)HDMI-CEC:一種可以通過HDMI在多種設(shè)備上進(jìn)行遠(yuǎn)程控制的技術(shù)。CEC(Consumer Electronics Control消費(fèi)電子控制)

3.TIF官方流程圖:

    

  如上圖所示,TVProvider和TV Input Manager就是TIF中的內(nèi)容,liveTVApp通過turning調(diào)用TV Input Manager獲得一個(gè)session,session里面放的是一路信源的狀態(tài),TV Input Manager 必須與 TV Input 創(chuàng)建一對(duì)一的會(huì)話。liveTVApp通過session以aidl的方式調(diào)用TVinputService獲得相關(guān)的頻道和具體的節(jié)目信息進(jìn)行播放,并提供家長(zhǎng)控制功能。TvInput將獲得的Channel和Programs信息寫入到/data/data/com.android.providers.tv/databases/tv.db數(shù)據(jù)庫(kù)中。

4.TIF為開發(fā)者提供的接口
1)TvView:負(fù)責(zé)顯示播放的內(nèi)容。它是一個(gè)ViewGroup的子類,它是切臺(tái)的入口,內(nèi)置surface用于顯示視頻播放的內(nèi)容和通過控制session可以控制音量的大小等。
2)TvInputService:TvInputService是一個(gè)重要的類,繼承了它并實(shí)現(xiàn)一些規(guī)范就可以實(shí)現(xiàn)一路input信源供其它應(yīng)用使用。在該service中要實(shí)現(xiàn)onCreatSession()方法該方法要返回一個(gè)TvInputService.Session對(duì)象。這里的service在Manifest中定義時(shí)要注意要添加permission和action。添加完之后系統(tǒng)的TvInputManager可以檢測(cè)到該service是一個(gè)TvInputService,也就是一路信源。
  下面創(chuàng)建一個(gè)自定義的Service,而這個(gè)Service要繼承系統(tǒng)的TvInputService,當(dāng)然為了簡(jiǎn)化這個(gè)過程我們可以使用android官方提供的TIF 隨播內(nèi)容庫(kù):

compile 'com.google.android.libraries.tv:companionlibrary:0.2'
public class TvService extends BaseTvInputService {
    @Nullable
    @Override
    public TvInputService.Session onCreateSession(@NonNull String inputId) {
        TvInputSessionImpl session = new TvInputSessionImpl(this, inputId);
        session.setOverlayViewEnabled(true);
        return session;
    }
}

這里的BaseTvInputService也是繼承的TvInputService,需要復(fù)寫onCreateSession方法,創(chuàng)建自己的Session用于和TvInputManager交互,最后在清單文件中配置如下:

<service
  android:name=".service.TvService"
  android:permission="android.permission.BIND_TV_INPUT">  <intent-filter>    <action android:name="android.media.tv.TvInputService" />  </intent-filter>  <meta-data
    android:name="android.media.tv.input"
    android:resource="@xml/richtvinputservice" /></service>

接著在xml/richtvinputservice中配置了兩個(gè)activty,這個(gè)是提供LiveTv去打開的,比如第一次啟動(dòng)這個(gè)源時(shí),需要啟動(dòng)到setupActivity所指定的activity,設(shè)置時(shí)需要啟動(dòng)到settingsActivity配置的activity.

<?xml version="1.0" encoding="utf-8"?>
<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
    android:settingsActivity="com.xray.tv.input.MainActivity"
    android:setupActivity="com.xray.tv.input.MainActivity" />

3)TvInputService.Sssion:該session類TvView通過Tune方法會(huì)指定相應(yīng)的inputId(往往是該service對(duì)應(yīng)的“包名/.類名”)和uri,uri中包含對(duì)應(yīng)的節(jié)目id,該tune方法會(huì)調(diào)用Session的Onturn方法中,在這個(gè)方法中解析傳過來(lái)的id,根據(jù)id利用TvProvider去查詢數(shù)據(jù)庫(kù)的數(shù)據(jù),設(shè)置給player,這里使用onSetSurface()方法將TvView創(chuàng)建的surface設(shè)置給player,然后player就在該surface上顯示內(nèi)容。
4)TvContract:介于TvProvider和TvApp之間的一層封裝,它里面封裝了一些uri。里面有兩個(gè)內(nèi)部類是兩個(gè)javaBean。他們分別是TvContract.channels(頻道表),TvContract.Programs(頻道里面的節(jié)目單,比如少兒頻道里面海賊王第5集,火影忍者第6集等)。
5)TvInputManager:這個(gè)是TIF的核心類,它是系統(tǒng)的類,可以監(jiān)測(cè)到在系統(tǒng)的service中注冊(cè)"android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS"action的類,并將其設(shè)為一路信源。它來(lái)管理一些回調(diào),比如video是否可用,video的大小尺寸是否變換。通過下面的代碼可以獲得一個(gè)TvInputManager:

TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);

  TvInputManager只是我們當(dāng)前進(jìn)程的代理,它的真正實(shí)現(xiàn)其實(shí)是一個(gè)系統(tǒng)的Service,所以我們可以知道這個(gè)service其實(shí)在system_server進(jìn)程中,在類TvInputManagerService中實(shí)現(xiàn)。由于這個(gè)地方是跨進(jìn)程通信,其實(shí)它使用的是aidl的方式,所以我們可以找到TvInputManager在aidl中定義的接口:

/**
 * Interface to the TV input manager service.
 * @hide
 */
interface ITvInputManager {
    List<TvInputInfo> getTvInputList(int userId);
    TvInputInfo getTvInputInfo(in String inputId, int userId);
    void updateTvInputInfo(in TvInputInfo inputInfo, int userId);
    int getTvInputState(in String inputId, int userId);

    List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId);

    void registerCallback(in ITvInputManagerCallback callback, int userId);
    void unregisterCallback(in ITvInputManagerCallback callback, int userId);

    boolean isParentalControlsEnabled(int userId);
    void setParentalControlsEnabled(boolean enabled, int userId);
    boolean isRatingBlocked(in String rating, int userId);
    List<String> getBlockedRatings(int userId);
    void addBlockedRating(in String rating, int userId);
    void removeBlockedRating(in String rating, int userId);

    void createSession(in ITvInputClient client, in String inputId, boolean isRecordingSession,
            int seq, int userId);
    void releaseSession(in IBinder sessionToken, int userId);
    int getClientPid(in String sessionId);

    void setMainSession(in IBinder sessionToken, int userId);
    void setSurface(in IBinder sessionToken, in Surface surface, int userId);
    void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
            int userId);
    void setVolume(in IBinder sessionToken, float volume, int userId);
    void tune(in IBinder sessionToken, in Uri channelUri, in Bundle params, int userId);
    void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
    void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);

    void sendAppPrivateCommand(in IBinder sessionToken, in String action, in Bundle data,
            int userId);

    void createOverlayView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
            int userId);
    void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
    void removeOverlayView(in IBinder sessionToken, int userId);

    void unblockContent(in IBinder sessionToken, in String unblockedRating, int userId);

    void timeShiftPlay(in IBinder sessionToken, in Uri recordedProgramUri, int userId);
    void timeShiftPause(in IBinder sessionToken, int userId);
    void timeShiftResume(in IBinder sessionToken, int userId);
    void timeShiftSeekTo(in IBinder sessionToken, long timeMs, int userId);
    void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
    void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);

    // For the recording session
    void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
    void stopRecording(in IBinder sessionToken, int userId);

    // For TV input hardware binding
    List<TvInputHardwareInfo> getHardwareList();
    ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
            in TvInputInfo info, int userId, String tvInputSessionId, int priorityHint);
    void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);

    // For TV input capturing
    List<TvStreamConfig> getAvailableTvStreamConfigList(in String inputId, int userId);
    boolean captureFrame(in String inputId, in Surface surface, in TvStreamConfig config,
            int userId);
    boolean isSingleSessionActive(int userId);

    // For DVB device binding
    List<DvbDeviceInfo> getDvbDeviceList();
    ParcelFileDescriptor openDvbDevice(in DvbDeviceInfo info, int device);

    // For preview channels and programs
    void sendTvInputNotifyIntent(in Intent intent, int userId);
    void requestChannelBrowsable(in Uri channelUri, int userId);

    // For CTS purpose only. Add/remove a TvInputHardware device
    void addHardwareDevice(in int deviceId);
    void removeHardwareDevice(in int deviceId);
}

它的實(shí)現(xiàn)是在TvInputManagerService的內(nèi)部類BinderService中:

    private final class BinderService extends ITvInputManager.Stub {
        @Override
        public List<TvInputInfo> getTvInputList(int userId) {
            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, "getTvInputList");
            final long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                    List<TvInputInfo> inputList = new ArrayList<>();
                    for (TvInputState state : userState.inputMap.values()) {
                        inputList.add(state.info);
                    }
                    return inputList;
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }

        @Override
        public TvInputInfo getTvInputInfo(String inputId, int userId) {
            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, "getTvInputInfo");
            final long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                    TvInputState state = userState.inputMap.get(inputId);
                    return state == null ? null : state.info;
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }

    ......
    }

TvInputManagerService是在SystemServer中啟動(dòng)的,具體在SystemServer類的startOtherServices方法中:

            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV)
                    || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
                t.traceBegin("StartTvInputManager");
                mSystemServiceManager.startService(TvInputManagerService.class);
                t.traceEnd();
            }

注意上面會(huì)判斷系統(tǒng)TV和LEANBACK的特征而決定是否啟動(dòng)TvInputManagerService,特征可在device/google/atv/permissions/tv_core_hardware.xml中進(jìn)行配置:

<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->

<permissions>

    <!-- These are the hardware components that all television devices must
         include.  Devices with optional hardware must also include extra hardware
         files.
    -->
    <feature name="android.hardware.audio.output" />
    <feature name="android.hardware.location" />
    <feature name="android.hardware.location.network" />
    <feature name="android.hardware.screen.landscape" />
    <feature name="android.hardware.type.television" />

    <feature name="android.software.backup" />
    <feature name="android.software.leanback" />
    <feature name="android.software.leanback_only" />
    <feature name="android.software.live_tv" />
    <feature name="android.software.picture_in_picture" notLowRam="true" />
    <feature name="android.software.activities_on_secondary_displays" notLowRam="true" />
    <feature name="android.software.voice_recognizers" notLowRam="true" />
    <feature name="android.software.input_methods" />
    <feature name="android.software.autofill" />

    <feature name="android.software.cts" />

</permissions>

得到TvInputManager后我們可以遍歷拿到系統(tǒng)當(dāng)前有多少個(gè)service是Tv信源:

List<TvInputInfo> list = tvInputManager.getTvInputList();
  for(TvInputInfo info:list){    
  Log.i(TAG, "id:" + info.getId());
}

6) TvInputInfo:TvInput的信息。包括頻道類型,圖標(biāo),名稱等信息。
  因?yàn)門vInput經(jīng)常是以第三方應(yīng)用的方式實(shí)現(xiàn)的,當(dāng)TvInput應(yīng)用安裝時(shí),TvInputManagerService會(huì)檢測(cè)安裝包中是否包含TvInputService。

private void registerBroadcastReceivers() {
        PackageMonitor monitor = new PackageMonitor() {
            private void buildTvInputList(String[] packages) {
                synchronized (mLock) {
                    if (mCurrentUserId == getChangingUserId()) {
                        buildTvInputListLocked(mCurrentUserId, packages);
                        buildTvContentRatingSystemListLocked(mCurrentUserId);
                    }
                }
            }
 
            @Override
            public void onPackageUpdateFinished(String packageName, int uid) {
                if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
                // This callback is invoked when the TV input is reinstalled.
                // In this case, isReplacing() always returns true.
                buildTvInputList(new String[] { packageName });
            }
 
          ...
    }

當(dāng)有安裝包安裝時(shí),監(jiān)測(cè)其中是否有TvInputService,并且權(quán)限符合則綁定這個(gè)Service.

private void buildTvInputListLocked(int userId, String[] updatedPackages) {
        UserState userState = getOrCreateUserStateLocked(userId);
        userState.packageSet.clear();
 
        if (DEBUG) Slog.d(TAG, "buildTvInputList");
        PackageManager pm = mContext.getPackageManager();
        List<ResolveInfo> services = pm.queryIntentServicesAsUser(
                new Intent(TvInputService.SERVICE_INTERFACE),
                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
                userId);
        List<TvInputInfo> inputList = new ArrayList<>();
        for (ResolveInfo ri : services) {
            ServiceInfo si = ri.serviceInfo;
            //檢測(cè)是否有android.permission.BIND_TV_INPUT這個(gè)權(quán)限
            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) {
                    // New hardware input found. Create a new ServiceState and connect to the
                    // service to populate the hardware list.
                    serviceState = new ServiceState(component, userId);
                    userState.serviceStateMap.put(component, serviceState);
                    updateServiceConnectionLocked(component, userId);
                } else {
                    inputList.addAll(serviceState.hardwareInputMap.values());
                }
            } else {
                try {
                    TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
                    inputList.add(info);
                } catch (Exception e) {
                    Slog.e(TAG, "failed to load TV input " + si.name, e);
                    continue;
                }
            }
            userState.packageSet.add(si.packageName);
        }
 
        Map<String, TvInputState> inputMap = new HashMap<>();
        for (TvInputInfo info : inputList) {
            if (DEBUG) {
                Slog.d(TAG, "add " + info.getId());
            }
            TvInputState inputState = userState.inputMap.get(info.getId());
            if (inputState == null) {
                inputState = new TvInputState();
            }
            inputState.info = info;
            inputMap.put(info.getId(), inputState);
        }
 
        for (String inputId : inputMap.keySet()) {
            if (!userState.inputMap.containsKey(inputId)) {
                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)) {
                        //綁定TvInputService
                        updateServiceConnectionLocked(component, userId);
                        notifyInputUpdatedLocked(userState, inputId);
                        break;
                    }
                }
            }
        }
        ...
    }

綁定第三方自定義的TvInputService:

private void updateServiceConnectionLocked(ComponentName component, int userId) {
        UserState userState = getOrCreateUserStateLocked(userId);
        ServiceState serviceState = userState.serviceStateMap.get(component);
        if (serviceState == null) {
            return;
        }
        if (serviceState.reconnecting) {
            if (!serviceState.sessionTokens.isEmpty()) {
                // wait until all the sessions are removed.
                return;
            }
            serviceState.reconnecting = false;
        }
 
        boolean shouldBind;
        if (userId == mCurrentUserId) {
            shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
        } else {
            // For a non-current user,
            // if sessionTokens is not empty, it contains recording sessions only
            // because other sessions must have been removed while switching user
            // and non-recording sessions are not created by createSession().
            shouldBind = !serviceState.sessionTokens.isEmpty();
        }
 
        if (serviceState.service == null && shouldBind) {
            // This means that the service is not yet connected but its state indicates that we
            // have pending requests. Then, connect the service.
            if (serviceState.bound) {
                // We have already bound to the service so we don't try to bind again until after we
                // unbind later on.
                return;
            }
            if (DEBUG) {
                Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
            }
            //bind 第三方應(yīng)用自定義的TvInputService
            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
            serviceState.bound = mContext.bindServiceAsUser(
                    i, serviceState.connection,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                    new UserHandle(userId));
        } else if (serviceState.service != null && !shouldBind) {
            // This means that the service is already connected but its state indicates that we have
            // nothing to do with it. Then, disconnect the service.
            if (DEBUG) {
                Slog.d(TAG, "unbindService(service=" + component + ")");
            }
            mContext.unbindService(serviceState.connection);
            userState.serviceStateMap.remove(component);
        }
    }

TvInputService現(xiàn)在綁定了,那么TvInputMangerService和TvInputService交互的邏輯就到了ServiceConnection中,它的實(shí)現(xiàn)在InputServiceConnection中:

        @Override
        public void onServiceConnected(ComponentName component, IBinder service) {
            if (DEBUG) {
                Slog.d(TAG, "onServiceConnected(component=" + component + ")");
            }
            synchronized (mLock) {
                UserState userState = mUserStates.get(mUserId);
                if (userState == null) {
                    // The user was removed while connecting.
                    mContext.unbindService(this);
                    return;
                }
                ServiceState serviceState = userState.serviceStateMap.get(mComponent);
                serviceState.service = ITvInputService.Stub.asInterface(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);
                    }
                }

                List<IBinder> tokensToBeRemoved = new ArrayList<>();

                // And create sessions, if any.
                for (IBinder sessionToken : serviceState.sessionTokens) {
                    if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
                        tokensToBeRemoved.add(sessionToken);
                    }
                }

                for (IBinder sessionToken : tokensToBeRemoved) {
                    removeSessionStateLocked(sessionToken, mUserId);
                }

                for (TvInputState inputState : userState.inputMap.values()) {
                    if (inputState.info.getComponent().equals(component)
                            && inputState.state != INPUT_STATE_CONNECTED) {
                        notifyInputStateChangedLocked(userState, inputState.info.getId(),
                                inputState.state, null);
                    }
                }

                if (serviceState.isHardware) {
                    serviceState.hardwareInputMap.clear();
                    for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) {
                        try {
                            serviceState.service.notifyHardwareAdded(hardware);
                        } catch (RemoteException e) {
                            Slog.e(TAG, "error in notifyHardwareAdded", e);
                        }
                    }
                    for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
                        try {
                            serviceState.service.notifyHdmiDeviceAdded(device);
                        } catch (RemoteException e) {
                            Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
                        }
                    }
                }
            }
        }

  在onServiceConnected成功后,就可以拿到從TvInputService中獲取的Binder對(duì)象,和第三方的TvInputService聯(lián)通進(jìn)行交互,它們之間交互需要?jiǎng)?chuàng)建一個(gè)Session,也就是TvInputService.Session,這個(gè)Session中的交互是通過ITvInputSessionCallback來(lái)實(shí)現(xiàn)。

7)TvInputCallback:TvView的一個(gè)內(nèi)部類,TvInputCallBack可以反饋給TvView一些信息比如連接service是否成功,Video是否可用等:
ITvInputSessionCallback.aidl

/**
 * Helper interface for ITvInputSession to allow the TV input to notify the system service when a
 * new session has been created.
 * @hide
 */
oneway interface ITvInputSessionCallback {
    void onSessionCreated(ITvInputSession session, in IBinder hardwareSessionToken);
    void onSessionEvent(in String name, in Bundle args);
    void onChannelRetuned(in Uri channelUri);
    void onTracksChanged(in List<TvTrackInfo> tracks);
    void onTrackSelected(int type, in String trackId);
    void onVideoAvailable();
    void onVideoUnavailable(int reason);
    void onContentAllowed();
    void onContentBlocked(in String rating);
    void onLayoutSurface(int left, int top, int right, int bottom);
    void onTimeShiftStatusChanged(int status);
    void onTimeShiftStartPositionChanged(long timeMs);
    void onTimeShiftCurrentPositionChanged(long timeMs);

    // For the recording session
    void onTuned(in Uri channelUri);
    void onRecordingStopped(in Uri recordedProgramUri);
    void onError(int error);
}

自定義第三方TvInputService時(shí),根據(jù)需求實(shí)現(xiàn)以上方法:

tvView.setCallback(new TvView.TvInputCallback() {    
    @Override    
    public void onConnectionFailed(String inputId) {
         super.onConnectionFailed(inputId);
         LogUtil.i(this,"MainActivity.onConnectionFailed:"+inputId); 
    }
    @Override    
    public void onDisconnected(String inputId) { 
        super.onDisconnected(inputId);
         LogUtil.i(this,"MainActivity.onDisconnected."); 
    }    
   @Override    
   public void onVideoSizeChanged(String inputId, int width, int height) { 
        super.onVideoSizeChanged(inputId, width, height); 
        LogUtil.i(this,"MainActivity.onVideoSizeChanged.");    
   }    
   @Override
     public void onVideoAvailable(String inputId) {
        super.onVideoAvailable(inputId);
        LogUtil.i(this,"MainActivity.onVideoAvailable.inputId:"+inputId);
    }
    @Override
    public void onVideoUnavailable(String inputId, int reason) {
        super.onVideoUnavailable(inputId, reason);
        LogUtil.i(this,"MainActivity.onVideoUnavailable.");
    }    
......
});

至此,TvInputManager和第三方TvInputService的交互就完成了。

8)TvProvider:

LiveTv和TvInput之間交互還有一種方式就是TvProvider, TvInput應(yīng)用會(huì)將自己的頻道和節(jié)目數(shù)據(jù)寫入TvProvider對(duì)應(yīng)的數(shù)據(jù)庫(kù)中,數(shù)據(jù)庫(kù)的位置在:/data/data/com.android.providers.tv/databases/tv.db

這樣LiveTv就可以讀取TvProvider中的數(shù)據(jù)了。當(dāng)然這里的數(shù)據(jù)除了LiveTv和當(dāng)前的TvInput應(yīng)用,其他應(yīng)用是沒有權(quán)限讀取的。

總結(jié)

以上是生活随笔為你收集整理的Android TV : 电视输入框架(TIF)剖析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。