13957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo/*
23957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * Copyright (C) 2014 The Android Open Source Project
33957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo *
43957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * Licensed under the Apache License, Version 2.0 (the "License");
53957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * you may not use this file except in compliance with the License.
63957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * You may obtain a copy of the License at
73957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo *
83957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo *      http://www.apache.org/licenses/LICENSE-2.0
93957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo *
103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * Unless required by applicable law or agreed to in writing, software
113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * distributed under the License is distributed on an "AS IS" BASIS,
123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * See the License for the specific language governing permissions and
143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo * limitations under the License.
153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo */
163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
17d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seopackage android.media.tv;
183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
19cdfbc488c675a9800dfc8f15aec24b65a7558d29Jae Seoimport android.annotation.FloatRange;
20a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.annotation.MainThread;
21c8b7356434f665c494504661a943323c0bbe702eJae Seoimport android.annotation.NonNull;
224bf607b00c14c031e991ac9dc0ad49b9249c9162Dongwon Kangimport android.annotation.Nullable;
232c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seoimport android.annotation.SuppressLint;
24187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport android.annotation.SystemApi;
25345af96677d5fbfc00f8f38a46ab7d57c1ff4cbbJae Seoimport android.app.ActivityManager;
263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.app.Service;
279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.content.Context;
283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Intent;
299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.graphics.PixelFormat;
309a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.graphics.Rect;
3161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
323d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seoimport android.media.PlaybackParams;
333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.net.Uri;
34ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kangimport android.os.AsyncTask;
35832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Choimport android.os.Bundle;
363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Handler;
373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.IBinder;
383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Message;
39ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kangimport android.os.Process;
403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.RemoteCallbackList;
413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.RemoteException;
42bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kangimport android.text.TextUtils;
433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.util.Log;
449a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.view.Gravity;
456a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputChannel;
466a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputDevice;
476a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputEvent;
486a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputEventReceiver;
496a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.KeyEvent;
506a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.MotionEvent;
513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.view.Surface;
529a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.view.View;
539a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.view.WindowManager;
542c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seoimport android.view.accessibility.CaptioningManager;
55ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kangimport android.widget.FrameLayout;
563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
576a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport com.android.internal.os.SomeArgs;
58de08be8f79ea40f3dffae9edff4227704a5c0a3aJae Seoimport com.android.internal.util.Preconditions;
593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
603eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kangimport java.util.ArrayList;
6110d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seoimport java.util.HashSet;
621f213914c45c23c653f721690da2ce0718e63139Dongwon Kangimport java.util.List;
6310d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seoimport java.util.Set;
641f213914c45c23c653f721690da2ce0718e63139Dongwon Kang
653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo/**
66782f7345471072b630e58c7abd3579b0015273b1Jae Seo * The TvInputService class represents a TV input or source such as HDMI or built-in tuner which
67782f7345471072b630e58c7abd3579b0015273b1Jae Seo * provides pass-through video or broadcast TV programs.
680610e12733875a267f59d87a2a68aebbf486066eDongwon Kang *
690610e12733875a267f59d87a2a68aebbf486066eDongwon Kang * <p>Applications will not normally use this service themselves, instead relying on the standard
70782f7345471072b630e58c7abd3579b0015273b1Jae Seo * interaction provided by {@link TvView}. Those implementing TV input services should normally do
71782f7345471072b630e58c7abd3579b0015273b1Jae Seo * so by deriving from this class and providing their own session implementation based on
72782f7345471072b630e58c7abd3579b0015273b1Jae Seo * {@link TvInputService.Session}. All TV input services must require that clients hold the
73782f7345471072b630e58c7abd3579b0015273b1Jae Seo * {@link android.Manifest.permission#BIND_TV_INPUT} in order to interact with the service; if this
74782f7345471072b630e58c7abd3579b0015273b1Jae Seo * permission is not specified in the manifest, the system will refuse to bind to that TV input
75782f7345471072b630e58c7abd3579b0015273b1Jae Seo * service.
763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo */
773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seopublic abstract class TvInputService extends Service {
78ee2ec05ed7c0d3cb9115f4ddd7c3613269c4a57bJae Seo    private static final boolean DEBUG = false;
793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final String TAG = "TvInputService";
803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
81e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho    private static final int DETACH_OVERLAY_VIEW_TIMEOUT_MS = 5000;
82e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho
833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    /**
843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo     * This is the interface name that a service implementing a TV input should say that it support
853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo     * -- that is, this is the action it uses for its intent filter. To be supported, the service
863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo     * must also require the {@link android.Manifest.permission#BIND_TV_INPUT} permission so that
873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo     * other applications cannot abuse it.
883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo     */
89d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seo    public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService";
903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
91e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee    /**
92e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee     * Name under which a TvInputService component publishes information about itself.
93e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee     * This meta-data must reference an XML resource containing an
94e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee     * <code>&lt;{@link android.R.styleable#TvInputService tv-input}&gt;</code>
95e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee     * tag.
96e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee     */
97e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee    public static final String SERVICE_META_DATA = "android.media.tv.input";
98e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee
9966b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang    /**
10066b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang     * Handler instance to handle request from TV Input Manager Service. Should be run in the main
10166b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang     * looper to be synchronously run with {@code Session.mHandler}.
10266b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang     */
103546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final Handler mServiceHandler = new ServiceHandler();
1043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
105093d994965bef197fb676731fc50f6f6f630b8feJae Seo            new RemoteCallbackList<>();
1063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
107911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang    private TvInputManager mTvInputManager;
108911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang
1093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    @Override
1103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public final IBinder onBind(Intent intent) {
1113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return new ITvInputService.Stub() {
1123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void registerCallback(ITvInputServiceCallback cb) {
1143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (cb != null) {
1153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    mCallbacks.register(cb);
1163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void unregisterCallback(ITvInputServiceCallback cb) {
1213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (cb != null) {
1223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    mCallbacks.unregister(cb);
1233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
127187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            public void createSession(InputChannel channel, ITvInputSessionCallback cb,
128187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    String inputId) {
1296a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                if (channel == null) {
1306a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    Log.w(TAG, "Creating session without input channel");
1316a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                }
1326a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                if (cb == null) {
1336a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    return;
1343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1356a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                SomeArgs args = SomeArgs.obtain();
1366a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                args.arg1 = channel;
1376a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                args.arg2 = cb;
138187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                args.arg3 = inputId;
13966b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang                mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
1403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
141187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
142187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            @Override
143a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            public void createRecordingSession(ITvInputSessionCallback cb, String inputId) {
144a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                if (cb == null) {
145a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    return;
146a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
147a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                SomeArgs args = SomeArgs.obtain();
148a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                args.arg1 = cb;
149a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                args.arg2 = inputId;
150a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_RECORDING_SESSION, args)
151a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        .sendToTarget();
152a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
153a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
154a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            @Override
155187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) {
1561abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_INPUT,
157187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        hardwareInfo).sendToTarget();
158187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
159187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
160187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            @Override
1614f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            public void notifyHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
1621abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HARDWARE_INPUT,
1634f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        hardwareInfo).sendToTarget();
1644f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
1654f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
1664f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            @Override
167546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            public void notifyHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
1681abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_INPUT,
169546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        deviceInfo).sendToTarget();
1704f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
1714f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
1724f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            @Override
173546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            public void notifyHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
1741abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_INPUT,
175546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        deviceInfo).sendToTarget();
176187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
1773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
1783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    /**
181782f7345471072b630e58c7abd3579b0015273b1Jae Seo     * Returns a concrete implementation of {@link Session}.
1820610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     *
1830610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     * <p>May return {@code null} if this TV input service fails to create a session for some
1840610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     * reason. If TV input represents an external device connected to a hardware TV input,
185bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang     * {@link HardwareSession} should be returned.
1860610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     *
187187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     * @param inputId The ID of the TV input associated with the session.
188187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     */
1894bf607b00c14c031e991ac9dc0ad49b9249c9162Dongwon Kang    @Nullable
1901032f034ff4f0506872e4899b5a232057abe724bWonsik Kim    public abstract Session onCreateSession(String inputId);
191187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
192187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    /**
193a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     * Returns a concrete implementation of {@link RecordingSession}.
194a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     *
195a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     * <p>May return {@code null} if this TV input service fails to create a recording session for
196a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     * some reason.
197a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     *
198a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     * @param inputId The ID of the TV input associated with the recording session.
199a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     */
200a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo    @Nullable
201533b34568280e61d885c3ea438fdb585ea2e4ba0Jae Seo    public RecordingSession onCreateRecordingSession(String inputId) {
202533b34568280e61d885c3ea438fdb585ea2e4ba0Jae Seo        return null;
203533b34568280e61d885c3ea438fdb585ea2e4ba0Jae Seo    }
204a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
205a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo    /**
2064f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * Returns a new {@link TvInputInfo} object if this service is responsible for
2074f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of
2084f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * ignoring all hardware input.
209187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     *
2104f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * @param hardwareInfo {@link TvInputHardwareInfo} object just added.
211187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     * @hide
212187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     */
2134bf607b00c14c031e991ac9dc0ad49b9249c9162Dongwon Kang    @Nullable
214187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    @SystemApi
215187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) {
216187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        return null;
217187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
218187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
219187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    /**
220187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     * Returns the input ID for {@code deviceId} if it is handled by this service;
2214f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * otherwise, return {@code null}. Override to modify default behavior of ignoring all hardware
2224f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * input.
223187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     *
2244f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * @param hardwareInfo {@link TvInputHardwareInfo} object just removed.
225187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     * @hide
226187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim     */
2274bf607b00c14c031e991ac9dc0ad49b9249c9162Dongwon Kang    @Nullable
228187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    @SystemApi
2294f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
2304f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        return null;
2314f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
2324f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
2334f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /**
2344f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * Returns a new {@link TvInputInfo} object if this service is responsible for
235546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo     * {@code deviceInfo}; otherwise, return {@code null}. Override to modify default behavior of
236546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo     * ignoring all HDMI logical input device.
2374f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     *
238546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo     * @param deviceInfo {@link HdmiDeviceInfo} object just added.
2394f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * @hide
2404f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     */
2414bf607b00c14c031e991ac9dc0ad49b9249c9162Dongwon Kang    @Nullable
2424f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    @SystemApi
243546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    public TvInputInfo onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
2444f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        return null;
2454f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
2464f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
2474f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /**
2488960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim     * Returns the input ID for {@code deviceInfo} if it is handled by this service; otherwise,
249546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo     * return {@code null}. Override to modify default behavior of ignoring all HDMI logical input
250546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo     * device.
2514f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     *
252546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo     * @param deviceInfo {@link HdmiDeviceInfo} object just removed.
2534f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     * @hide
2544f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee     */
2554bf607b00c14c031e991ac9dc0ad49b9249c9162Dongwon Kang    @Nullable
2564f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    @SystemApi
257546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    public String onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
258187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        return null;
259187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
260187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
261911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang    private boolean isPassthroughInput(String inputId) {
262911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang        if (mTvInputManager == null) {
263911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang            mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
264911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang        }
265911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang        TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
2666e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        return info != null && info.isPassthroughInput();
267911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang    }
268911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang
269187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    /**
270b8a64416e5e7cf39fd899fa600a940b0ef3c15fdJae Seo     * Base class for derived classes to implement to provide a TV input session.
2713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo     */
27266b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang    public abstract static class Session implements KeyEvent.Callback {
2736f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        private static final int POSITION_UPDATE_INTERVAL_MS = 1000;
2746f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
2756a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
2769a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        private final WindowManager mWindowManager;
27766b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang        final Handler mHandler;
2789a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        private WindowManager.LayoutParams mWindowParams;
279f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho        private Surface mSurface;
280c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo        private final Context mContext;
281ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang        private FrameLayout mOverlayViewContainer;
2829a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        private View mOverlayView;
283ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang        private OverlayViewCleanUpTask mOverlayViewCleanUpTask;
2849a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        private boolean mOverlayViewEnabled;
2859a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        private IBinder mWindowToken;
2869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        private Rect mOverlayFrame;
28773d86e34bb2ce73c88f1a3496b71fe34113d1296Conrad Chen        private long mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
28873d86e34bb2ce73c88f1a3496b71fe34113d1296Conrad Chen        private long mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
289465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        private final TimeShiftPositionTrackingRunnable
290465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                mTimeShiftPositionTrackingRunnable = new TimeShiftPositionTrackingRunnable();
2913eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang
292c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo        private final Object mLock = new Object();
2933eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang        // @GuardedBy("mLock")
294832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho        private ITvInputSessionCallback mSessionCallback;
2953eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang        // @GuardedBy("mLock")
296c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo        private final List<Runnable> mPendingActions = new ArrayList<>();
2979a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
29866b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang        /**
29966b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang         * Creates a new Session.
30066b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang         *
30166b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang         * @param context The context of the application
30266b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang         */
30366b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang        public Session(Context context) {
304ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            mContext = context;
30566b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
30666b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang            mHandler = new Handler(context.getMainLooper());
3079a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
3089a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
3096a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
310ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         * Enables or disables the overlay view.
311ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         *
312ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         * <p>By default, the overlay view is disabled. Must be called explicitly after the
313ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         * session is created to enable the overlay view.
314ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         *
315ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         * <p>The TV input service can disable its overlay view when the size of the overlay view is
316ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         * insufficient to display the whole information, such as when used in Picture-in-picture.
317ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         * Override {@link #onOverlayViewSizeChanged} to get the size of the overlay view, which
318ba2a1c3b92631a65d6dd1916a9d28368e1f5f589Dongwon Kang         * then can be used to determine whether to enable/disable the overlay view.
3196a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *
3206a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param enable {@code true} if you want to enable the overlay view. {@code false}
3216a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *            otherwise.
3226a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
3239a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void setOverlayViewEnabled(final boolean enable) {
3249a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mHandler.post(new Runnable() {
3259a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                @Override
3269a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                public void run() {
3279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    if (enable == mOverlayViewEnabled) {
3289a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        return;
3299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
3309a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    mOverlayViewEnabled = enable;
3319a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    if (enable) {
3329a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        if (mWindowToken != null) {
3339a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                            createOverlayView(mWindowToken, mOverlayFrame);
3349a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        }
3359a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } else {
3369a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        removeOverlayView(false);
3379a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
3389a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
3399a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            });
3409a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
3419a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
3423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        /**
343832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho         * Dispatches an event to the application using this session.
344832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho         *
345832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho         * @param eventType The type of the event.
346832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho         * @param eventArgs Optional arguments of the event.
347832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho         * @hide
348832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho         */
3493eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang        @SystemApi
350de08be8f79ea40f3dffae9edff4227704a5c0a3aJae Seo        public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) {
351de08be8f79ea40f3dffae9edff4227704a5c0a3aJae Seo            Preconditions.checkNotNull(eventType);
352a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
353832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                @Override
354832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                public void run() {
355832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    try {
35691a801d42f3acc35404da51ba26605093922503aJae Seo                        if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")");
357711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
358711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                            mSessionCallback.onSessionEvent(eventType, eventArgs);
359711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
360832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    } catch (RemoteException e) {
361465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in sending event (event=" + eventType + ")", e);
362832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
363832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                }
364832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            });
365832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho        }
366832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
367832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho        /**
3684640d3d81a182e8f3ce50199bff1d97519d27938Jae Seo         * Informs the application that the current channel is re-tuned for some reason and the
3694640d3d81a182e8f3ce50199bff1d97519d27938Jae Seo         * session now displays the content from a new channel. This is used to handle special cases
3704640d3d81a182e8f3ce50199bff1d97519d27938Jae Seo         * such as when the current channel becomes unavailable, it is necessary to send the user to
3714640d3d81a182e8f3ce50199bff1d97519d27938Jae Seo         * a certain channel or the user changes channel in some other way (e.g. by using a
3724640d3d81a182e8f3ce50199bff1d97519d27938Jae Seo         * dedicated remote).
373a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang         *
3744640d3d81a182e8f3ce50199bff1d97519d27938Jae Seo         * @param channelUri The URI of the new channel.
375a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang         */
37691a801d42f3acc35404da51ba26605093922503aJae Seo        public void notifyChannelRetuned(final Uri channelUri) {
377a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
378a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
379a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                @Override
380a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                public void run() {
381a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    try {
38291a801d42f3acc35404da51ba26605093922503aJae Seo                        if (DEBUG) Log.d(TAG, "notifyChannelRetuned");
383711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
384711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                            mSessionCallback.onChannelRetuned(channelUri);
385711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
386a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    } catch (RemoteException e) {
387465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyChannelRetuned", e);
388a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
389a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                }
390a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            });
391a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang        }
392a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang
393a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang        /**
3948ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * Sends the list of all audio/video/subtitle tracks. The is used by the framework to
3958ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * maintain the track information for a given session, which in turn is used by
3968ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * {@link TvView#getTracks} for the application to retrieve metadata for a given track type.
3978ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * The TV input service must call this method as soon as the track information becomes
3988ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * available or is updated. Note that in a case where a part of the information for a
3998ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * certain track is updated, it is not necessary to create a new {@link TvTrackInfo} object
4008ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * with a different track ID.
401b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang         *
4021f213914c45c23c653f721690da2ce0718e63139Dongwon Kang         * @param tracks A list which includes track information.
40310d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         */
40410d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo        public void notifyTracksChanged(final List<TvTrackInfo> tracks) {
4055a3ef42422ea542fce1d8d1d5b0fbf61d4a570a6Terry Heo            final List<TvTrackInfo> tracksCopy = new ArrayList<>(tracks);
406a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
407a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
408b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang                @Override
409b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang                public void run() {
410b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang                    try {
41110d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo                        if (DEBUG) Log.d(TAG, "notifyTracksChanged");
412711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
4135a3ef42422ea542fce1d8d1d5b0fbf61d4a570a6Terry Heo                            mSessionCallback.onTracksChanged(tracksCopy);
414711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
415b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang                    } catch (RemoteException e) {
416465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyTracksChanged", e);
417b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang                    }
418b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang                }
419b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang            });
420b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang        }
421b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang
422b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang        /**
4238ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * Sends the type and ID of a selected track. This is used to inform the application that a
4248ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * specific track is selected. The TV input service must call this method as soon as a track
4258ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * is selected either by default or in response to a call to {@link #onSelectTrack}. The
4268ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * selected track ID for a given type is maintained in the framework until the next call to
4278ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * this method even after the entire track list is updated (but is reset when the session is
4288ddb01ec7b1360e9fccf341b5f1989182160fceeJae Seo         * tuned to a new channel), so care must be taken not to result in an obsolete track ID.
429d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo         *
43010d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         * @param type The type of the selected track. The type can be
43110d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
43210d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         *            {@link TvTrackInfo#TYPE_SUBTITLE}.
43310d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         * @param trackId The ID of the selected track.
43410d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         * @see #onSelectTrack
435d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo         */
43610d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo        public void notifyTrackSelected(final int type, final String trackId) {
437a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
438a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
439d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo                @Override
440d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo                public void run() {
441d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo                    try {
44210d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo                        if (DEBUG) Log.d(TAG, "notifyTrackSelected");
443711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
444711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                            mSessionCallback.onTrackSelected(type, trackId);
445711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
446d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo                    } catch (RemoteException e) {
447465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyTrackSelected", e);
448d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo                    }
449d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo                }
450d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo            });
451d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo        }
452d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo
453d5ce9759524740cfb02638fd1d7b44315957b422Jae Seo        /**
4541bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         * Informs the application that the video is now available for watching. Video is blocked
4551bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         * until this method is called.
4561bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         *
4571bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         * <p>The TV input service must call this method as soon as the content rendered onto its
458e12d810e99da093d3cf38f89c81e3e8d1e75b404Dongwon Kang         * surface is ready for viewing. This method must be called each time {@link #onTune}
4591bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         * is called.
460d254611778be6bcaf091f1b081caf839dedf79acJae Seo         *
461d254611778be6bcaf091f1b081caf839dedf79acJae Seo         * @see #notifyVideoUnavailable
4629b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang         */
46391a801d42f3acc35404da51ba26605093922503aJae Seo        public void notifyVideoAvailable() {
464a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
465a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
4669b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                @Override
4679b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                public void run() {
4689b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    try {
46991a801d42f3acc35404da51ba26605093922503aJae Seo                        if (DEBUG) Log.d(TAG, "notifyVideoAvailable");
470711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
471711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                            mSessionCallback.onVideoAvailable();
472711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
4739b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    } catch (RemoteException e) {
474465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyVideoAvailable", e);
4759b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    }
4769b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                }
4779b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang            });
4789b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang        }
4799b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang
4809b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang        /**
481d254611778be6bcaf091f1b081caf839dedf79acJae Seo         * Informs the application that the video became unavailable for some reason. This is
482d254611778be6bcaf091f1b081caf839dedf79acJae Seo         * primarily used to signal the application to block the screen not to show any intermittent
483d254611778be6bcaf091f1b081caf839dedf79acJae Seo         * video artifacts.
484bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo         *
485d254611778be6bcaf091f1b081caf839dedf79acJae Seo         * @param reason The reason why the video became unavailable:
486d254611778be6bcaf091f1b081caf839dedf79acJae Seo         *            <ul>
487d254611778be6bcaf091f1b081caf839dedf79acJae Seo         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
488d254611778be6bcaf091f1b081caf839dedf79acJae Seo         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
489d254611778be6bcaf091f1b081caf839dedf79acJae Seo         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
490d254611778be6bcaf091f1b081caf839dedf79acJae Seo         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
491ff1f29e1b112e68d16908b1a89225315089f8e50Dongwon Kang         *            <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY}
492d254611778be6bcaf091f1b081caf839dedf79acJae Seo         *            </ul>
493d254611778be6bcaf091f1b081caf839dedf79acJae Seo         * @see #notifyVideoAvailable
494bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo         */
4953b9be6700fd631e25559693820d03389f8de3893Jae Seo        public void notifyVideoUnavailable(
4963b9be6700fd631e25559693820d03389f8de3893Jae Seo                @TvInputManager.VideoUnavailableReason final int reason) {
497bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo            if (reason < TvInputManager.VIDEO_UNAVAILABLE_REASON_START
498bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo                    || reason > TvInputManager.VIDEO_UNAVAILABLE_REASON_END) {
4998a151a0a2143cd962764f74a75cf4e587f6759d1Jae Seo                Log.e(TAG, "notifyVideoUnavailable - unknown reason: " + reason);
500bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo            }
501a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
502a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
503bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo                @Override
504bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo                public void run() {
505bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo                    try {
50691a801d42f3acc35404da51ba26605093922503aJae Seo                        if (DEBUG) Log.d(TAG, "notifyVideoUnavailable");
507711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
508711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                            mSessionCallback.onVideoUnavailable(reason);
509711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
510bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo                    } catch (RemoteException e) {
511465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyVideoUnavailable", e);
512bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo                    }
513bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo                }
514bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo            });
515bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo        }
516bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo
517bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo        /**
518bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo         * Informs the application that the user is allowed to watch the current program content.
5190610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
5200610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Each TV input service is required to query the system whether the user is allowed to
521783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * watch the current program before showing it to the user if the parental controls is
522783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * enabled (i.e. {@link TvInputManager#isParentalControlsEnabled
523783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input
524783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * service should block the content or not is determined by invoking
525783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)}
526783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * with the content rating for the current program. Then the {@link TvInputManager} makes a
527783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * judgment based on the user blocked ratings stored in the secure settings and returns the
528783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * result. If the rating in question turns out to be allowed by the user, the TV input
529783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * service must call this method to notify the application that is permitted to show the
530783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * content.
5310610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
5320610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Each TV input service also needs to continuously listen to any changes made to the
533783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * parental controls settings by registering a broadcast receiver to receive
534783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * {@link TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} and
535783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately
536783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * reevaluate the current program with the new parental controls settings.
5376057102dbb746593a7d59cf377c969b62e38c664Jae Seo         *
53891a801d42f3acc35404da51ba26605093922503aJae Seo         * @see #notifyContentBlocked
539783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * @see TvInputManager
5406057102dbb746593a7d59cf377c969b62e38c664Jae Seo         */
54191a801d42f3acc35404da51ba26605093922503aJae Seo        public void notifyContentAllowed() {
542a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
543a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
5446057102dbb746593a7d59cf377c969b62e38c664Jae Seo                @Override
5456057102dbb746593a7d59cf377c969b62e38c664Jae Seo                public void run() {
5466057102dbb746593a7d59cf377c969b62e38c664Jae Seo                    try {
54791a801d42f3acc35404da51ba26605093922503aJae Seo                        if (DEBUG) Log.d(TAG, "notifyContentAllowed");
548711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
549711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                            mSessionCallback.onContentAllowed();
550711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
5516057102dbb746593a7d59cf377c969b62e38c664Jae Seo                    } catch (RemoteException e) {
552465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyContentAllowed", e);
5536057102dbb746593a7d59cf377c969b62e38c664Jae Seo                    }
5546057102dbb746593a7d59cf377c969b62e38c664Jae Seo                }
5556057102dbb746593a7d59cf377c969b62e38c664Jae Seo            });
5566057102dbb746593a7d59cf377c969b62e38c664Jae Seo        }
5576057102dbb746593a7d59cf377c969b62e38c664Jae Seo
5586057102dbb746593a7d59cf377c969b62e38c664Jae Seo        /**
559bbcd206a798c8c2845200daf7a2d4cb7b29056f3Jae Seo         * Informs the application that the current program content is blocked by parent controls.
5600610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
5610610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Each TV input service is required to query the system whether the user is allowed to
562783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * watch the current program before showing it to the user if the parental controls is
563783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * enabled (i.e. {@link TvInputManager#isParentalControlsEnabled
564783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input
565783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * service should block the content or not is determined by invoking
566783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)}
567936c040ec445afad98ec16fc634ae6573eceefbbJae Seo         * with the content rating for the current program or {@link TvContentRating#UNRATED} in
568936c040ec445afad98ec16fc634ae6573eceefbbJae Seo         * case the rating information is missing. Then the {@link TvInputManager} makes a judgment
569936c040ec445afad98ec16fc634ae6573eceefbbJae Seo         * based on the user blocked ratings stored in the secure settings and returns the result.
570936c040ec445afad98ec16fc634ae6573eceefbbJae Seo         * If the rating in question turns out to be blocked, the TV input service must immediately
571936c040ec445afad98ec16fc634ae6573eceefbbJae Seo         * block the content and call this method with the content rating of the current program to
572936c040ec445afad98ec16fc634ae6573eceefbbJae Seo         * prompt the PIN verification screen.
5730610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
5740610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Each TV input service also needs to continuously listen to any changes made to the
575783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * parental controls settings by registering a broadcast receiver to receive
576783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * {@link TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} and
577783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately
578783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * reevaluate the current program with the new parental controls settings.
5799b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang         *
580936c040ec445afad98ec16fc634ae6573eceefbbJae Seo         * @param rating The content rating for the current TV program. Can be
581936c040ec445afad98ec16fc634ae6573eceefbbJae Seo         *            {@link TvContentRating#UNRATED}.
58291a801d42f3acc35404da51ba26605093922503aJae Seo         * @see #notifyContentAllowed
583783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo         * @see TvInputManager
5849b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang         */
585c8b7356434f665c494504661a943323c0bbe702eJae Seo        public void notifyContentBlocked(@NonNull final TvContentRating rating) {
586de08be8f79ea40f3dffae9edff4227704a5c0a3aJae Seo            Preconditions.checkNotNull(rating);
587a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
588a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
5899b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                @Override
5909b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                public void run() {
5919b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    try {
59291a801d42f3acc35404da51ba26605093922503aJae Seo                        if (DEBUG) Log.d(TAG, "notifyContentBlocked");
593711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
594711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                            mSessionCallback.onContentBlocked(rating.flattenToString());
595711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
5969b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    } catch (RemoteException e) {
597465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyContentBlocked", e);
5989b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    }
5999b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                }
6009b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang            });
6019b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang        }
6029b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang
6039b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang        /**
604465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * Informs the application that the time shift status is changed.
6050610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
6060610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Prior to calling this method, the application assumes the status
607465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * {@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN}. Right after the session is created, it
608465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * is important to invoke the method with the status
609465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} if the implementation does support
610465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * time shifting, or {@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED} otherwise. Failure
611465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * to notifying the current status change immediately might result in an undesirable
612465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * behavior in the application such as hiding the play controls.
6130610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
6140610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>If the status {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} is reported, the
615465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * application assumes it can pause/resume playback, seek to a specified time position and
616465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * set playback rate and audio mode. The implementation should override
617465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * {@link #onTimeShiftPause}, {@link #onTimeShiftResume}, {@link #onTimeShiftSeekTo},
618465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * {@link #onTimeShiftGetStartPosition}, {@link #onTimeShiftGetCurrentPosition} and
6193d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo         * {@link #onTimeShiftSetPlaybackParams}.
6206f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         *
621465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * @param status The current time shift status. Should be one of the followings.
6226f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         * <ul>
623465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
6246f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
625465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
6266f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         * </ul>
6276f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
6283b9be6700fd631e25559693820d03389f8de3893Jae Seo        public void notifyTimeShiftStatusChanged(@TvInputManager.TimeShiftStatus final int status) {
629a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
630a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
6316f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                @Override
6326f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                public void run() {
63373d86e34bb2ce73c88f1a3496b71fe34113d1296Conrad Chen                    timeShiftEnablePositionTracking(
63473d86e34bb2ce73c88f1a3496b71fe34113d1296Conrad Chen                            status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE);
6356f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
6366f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        if (DEBUG) Log.d(TAG, "notifyTimeShiftStatusChanged");
6376f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        if (mSessionCallback != null) {
6386f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                            mSessionCallback.onTimeShiftStatusChanged(status);
6396f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        }
6406f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException e) {
641465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyTimeShiftStatusChanged", e);
6426f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
6436f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
6446f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            });
6456f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
6466f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
647465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        private void notifyTimeShiftStartPositionChanged(final long timeMs) {
648a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
649a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
6506f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                @Override
6516f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                public void run() {
6526f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
6536f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        if (DEBUG) Log.d(TAG, "notifyTimeShiftStartPositionChanged");
6546f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        if (mSessionCallback != null) {
6556f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                            mSessionCallback.onTimeShiftStartPositionChanged(timeMs);
6566f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        }
6576f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException e) {
658465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyTimeShiftStartPositionChanged", e);
6596f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
6606f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
6616f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            });
6626f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
6636f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
6646f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        private void notifyTimeShiftCurrentPositionChanged(final long timeMs) {
665a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
666a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
6676f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                @Override
6686f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                public void run() {
6696f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
6706f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        if (DEBUG) Log.d(TAG, "notifyTimeShiftCurrentPositionChanged");
6716f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        if (mSessionCallback != null) {
6726f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                            mSessionCallback.onTimeShiftCurrentPositionChanged(timeMs);
6736f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        }
6746f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException e) {
675465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in notifyTimeShiftCurrentPositionChanged", e);
6766f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
6776f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
6786f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            });
6796f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
6806f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
6816f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
682606c8a396558e9714159db4969340af170677172Jae Seo         * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
683606c8a396558e9714159db4969340af170677172Jae Seo         * is relative to the overlay view that sits on top of this surface.
684ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         *
685ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         * @param left Left position in pixels, relative to the overlay view.
686ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         * @param top Top position in pixels, relative to the overlay view.
687ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         * @param right Right position in pixels, relative to the overlay view.
68879d2b4c810f9359640a0aaa80e610508481cd413Dongwon Kang         * @param bottom Bottom position in pixels, relative to the overlay view.
6895b1caaf7d8408bf0ce78d8d7a36f4649dda17797Jae Seo         * @see #onOverlayViewSizeChanged
690ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         */
69179d2b4c810f9359640a0aaa80e610508481cd413Dongwon Kang        public void layoutSurface(final int left, final int top, final int right,
69279d2b4c810f9359640a0aaa80e610508481cd413Dongwon Kang                final int bottom) {
69379d2b4c810f9359640a0aaa80e610508481cd413Dongwon Kang            if (left > right || top > bottom) {
694ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                throw new IllegalArgumentException("Invalid parameter");
695ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho            }
696a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
697a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
698ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                @Override
699ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                public void run() {
700ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                    try {
701ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                        if (DEBUG) Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top + ", r="
70279d2b4c810f9359640a0aaa80e610508481cd413Dongwon Kang                                + right + ", b=" + bottom + ",)");
703711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        if (mSessionCallback != null) {
704711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                            mSessionCallback.onLayoutSurface(left, top, right, bottom);
705711914421896ad15ab3c944c3adc838ac67cf2a6Jae Seo                        }
706ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                    } catch (RemoteException e) {
707465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.w(TAG, "error in layoutSurface", e);
708ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                    }
709ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                }
710ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho            });
711ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho        }
712ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho
713ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho        /**
7143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         * Called when the session is released.
7153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         */
7163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public abstract void onRelease();
7173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        /**
71915c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * Sets the current session as the main session. The main session is a session whose
72015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * corresponding TV input determines the HDMI-CEC active source device.
7210610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
7220610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>TV input service that manages HDMI-CEC logical device should implement {@link
72315c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * #onSetMain} to (1) select the corresponding HDMI logical device as the source device
72415c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * when {@code isMain} is {@code true}, and to (2) select the internal device (= TV itself)
72515c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * as the source device when {@code isMain} is {@code false} and the session is still main.
72615c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * Also, if a surface is passed to a non-main session and active source is changed to
72715c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * initiate the surface, the active source should be returned to the main session.
7280610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
7290610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>{@link TvView} guarantees that, when tuning involves a session transition, {@code
73015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * onSetMain(true)} for new session is called first, {@code onSetMain(false)} for old
73115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * session is called afterwards. This allows {@code onSetMain(false)} to be no-op when TV
73215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * input service knows that the next main session corresponds to another HDMI logical
73315c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * device. Practically, this implies that one TV input service should handle all HDMI port
73415c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * and HDMI-CEC logical devices for smooth active source transition.
7354c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee         *
73615c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * @param isMain If true, session should become main.
73715c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * @see TvView#setMain
7384c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee         * @hide
7394c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee         */
7404c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        @SystemApi
74115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee        public void onSetMain(boolean isMain) {
7424c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        }
7434c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee
744c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo        /**
745606c8a396558e9714159db4969340af170677172Jae Seo         * Called when the application sets the surface.
7460610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
747606c8a396558e9714159db4969340af170677172Jae Seo         * <p>The TV input service should render video onto the given surface. When called with
748a9b6289faaf0ad3768fb8530b199fe7a44a241abConrad Chen         * {@code null}, the input service should immediately free any references to the
749606c8a396558e9714159db4969340af170677172Jae Seo         * currently set surface and stop using it.
750c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo         *
751606c8a396558e9714159db4969340af170677172Jae Seo         * @param surface The surface to be used for video rendering. Can be {@code null}.
752606c8a396558e9714159db4969340af170677172Jae Seo         * @return {@code true} if the surface was set successfully, {@code false} otherwise.
753c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo         */
7544bf607b00c14c031e991ac9dc0ad49b9249c9162Dongwon Kang        public abstract boolean onSetSurface(@Nullable Surface surface);
7553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        /**
757606c8a396558e9714159db4969340af170677172Jae Seo         * Called after any structural changes (format or size) have been made to the surface passed
758606c8a396558e9714159db4969340af170677172Jae Seo         * in {@link #onSetSurface}. This method is always called at least once, after
759606c8a396558e9714159db4969340af170677172Jae Seo         * {@link #onSetSurface} is called with non-null surface.
760e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho         *
761606c8a396558e9714159db4969340af170677172Jae Seo         * @param format The new PixelFormat of the surface.
762606c8a396558e9714159db4969340af170677172Jae Seo         * @param width The new width of the surface.
763606c8a396558e9714159db4969340af170677172Jae Seo         * @param height The new height of the surface.
764e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho         */
765e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        public void onSurfaceChanged(int format, int width, int height) {
766e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        }
767e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho
768e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        /**
769606c8a396558e9714159db4969340af170677172Jae Seo         * Called when the size of the overlay view is changed by the application.
770606c8a396558e9714159db4969340af170677172Jae Seo         *
771606c8a396558e9714159db4969340af170677172Jae Seo         * <p>This is always called at least once when the session is created regardless of whether
772606c8a396558e9714159db4969340af170677172Jae Seo         * the overlay view is enabled or not. The overlay view size is the same as the containing
773606c8a396558e9714159db4969340af170677172Jae Seo         * {@link TvView}. Note that the size of the underlying surface can be different if the
774606c8a396558e9714159db4969340af170677172Jae Seo         * surface was changed by calling {@link #layoutSurface}.
775ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         *
776ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         * @param width The width of the overlay view.
777ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         * @param height The height of the overlay view.
778ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho         */
779ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho        public void onOverlayViewSizeChanged(int width, int height) {
780ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho        }
781ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho
782ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho        /**
7831da8fb39499e8f5a962f7307fefdfd2ab6b79224Jae Seo         * Sets the relative stream volume of the current TV input session.
7843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         *
7851da8fb39499e8f5a962f7307fefdfd2ab6b79224Jae Seo         * <p>The implementation should honor this request in order to handle audio focus changes or
7861da8fb39499e8f5a962f7307fefdfd2ab6b79224Jae Seo         * mute the current session when multiple sessions, possibly from different inputs are
7871da8fb39499e8f5a962f7307fefdfd2ab6b79224Jae Seo         * active. If the method has not yet been called, the implementation should assume the
7881da8fb39499e8f5a962f7307fefdfd2ab6b79224Jae Seo         * default value of {@code 1.0f}.
7891da8fb39499e8f5a962f7307fefdfd2ab6b79224Jae Seo         *
7901da8fb39499e8f5a962f7307fefdfd2ab6b79224Jae Seo         * @param volume A volume value between {@code 0.0f} to {@code 1.0f}.
7913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         */
792cdfbc488c675a9800dfc8f15aec24b65a7558d29Jae Seo        public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
7933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        /**
7951bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         * Tunes to a given channel.
7961bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         *
7971bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         * <p>No video will be displayed until {@link #notifyVideoAvailable()} is called.
7981bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         * Also, {@link #notifyVideoUnavailable(int)} should be called when the TV input cannot
7991bccd280f59b86d00c04dcc9d827d76b373c600aNick Chalko         * continue playing the given channel.
8003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         *
8013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         * @param channelUri The URI of the channel.
802c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo         * @return {@code true} if the tuning was successful, {@code false} otherwise.
8033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         */
8043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public abstract boolean onTune(Uri channelUri);
8053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        /**
807031d7e838d9f026248e9e54a4aa025d2605bce78Dongwon Kang         * Tunes to a given channel. Override this method in order to handle domain-specific
808e12d810e99da093d3cf38f89c81e3e8d1e75b404Dongwon Kang         * features that are only known between certain TV inputs and their clients.
8091a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim         *
810031d7e838d9f026248e9e54a4aa025d2605bce78Dongwon Kang         * <p>The default implementation calls {@link #onTune(Uri)}.
811031d7e838d9f026248e9e54a4aa025d2605bce78Dongwon Kang         *
8121a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim         * @param channelUri The URI of the channel.
813e12d810e99da093d3cf38f89c81e3e8d1e75b404Dongwon Kang         * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
814e12d810e99da093d3cf38f89c81e3e8d1e75b404Dongwon Kang         *            name, i.e. prefixed with a package name you own, so that different developers
815e12d810e99da093d3cf38f89c81e3e8d1e75b404Dongwon Kang         *            will not create conflicting keys.
816c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo         * @return {@code true} if the tuning was successful, {@code false} otherwise.
8171a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim         */
8181a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim        public boolean onTune(Uri channelUri, Bundle params) {
8191a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim            return onTune(channelUri);
8201a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim        }
8211a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim
8221a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim        /**
8232c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         * Enables or disables the caption.
8240610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
8250610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>The locale for the user's preferred captioning language can be obtained by calling
8262c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         * {@link CaptioningManager#getLocale CaptioningManager.getLocale()}.
8272c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         *
8282c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         * @param enabled {@code true} to enable, {@code false} to disable.
8292c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         * @see CaptioningManager
8302c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         */
8312c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        public abstract void onSetCaptionEnabled(boolean enabled);
8322c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo
8332c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        /**
8349bf671f8ee72b156f16fcf05a3d1c6e093ecba67Sungsoo Lim         * Requests to unblock the content according to the given rating.
8350610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
8360610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>The implementation should unblock the content.
837903d6b72cd572665309633e925485464d08bb25aJaewan Kim         * TV input service has responsibility to decide when/how the unblock expires
838903d6b72cd572665309633e925485464d08bb25aJaewan Kim         * while it can keep previously unblocked ratings in order not to ask a user
839903d6b72cd572665309633e925485464d08bb25aJaewan Kim         * to unblock whenever a content rating is changed.
840903d6b72cd572665309633e925485464d08bb25aJaewan Kim         * Therefore an unblocked rating can be valid for a channel, a program,
841903d6b72cd572665309633e925485464d08bb25aJaewan Kim         * or certain amount of time depending on the implementation.
842903d6b72cd572665309633e925485464d08bb25aJaewan Kim         *
843903d6b72cd572665309633e925485464d08bb25aJaewan Kim         * @param unblockedRating An unblocked content rating
844903d6b72cd572665309633e925485464d08bb25aJaewan Kim         */
84591a801d42f3acc35404da51ba26605093922503aJae Seo        public void onUnblockContent(TvContentRating unblockedRating) {
8469bf671f8ee72b156f16fcf05a3d1c6e093ecba67Sungsoo Lim        }
847903d6b72cd572665309633e925485464d08bb25aJaewan Kim
848903d6b72cd572665309633e925485464d08bb25aJaewan Kim        /**
849c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo         * Selects a given track.
8500610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
8510610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>If this is done successfully, the implementation should call
8520610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * {@link #notifyTrackSelected} to help applications maintain the up-to-date list of the
8530610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * selected tracks.
8542c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         *
85510d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         * @param trackId The ID of the track to select. {@code null} means to unselect the current
85610d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         *            track for a given type.
85710d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         * @param type The type of the track to select. The type can be
85810d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
85910d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         *            {@link TvTrackInfo#TYPE_SUBTITLE}.
860c6a1e5d16c9e1fab5e597f308c3886e512791289Jae Seo         * @return {@code true} if the track selection was successful, {@code false} otherwise.
86110d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         * @see #notifyTrackSelected
86210d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo         */
8634bf607b00c14c031e991ac9dc0ad49b9249c9162Dongwon Kang        public boolean onSelectTrack(int type, @Nullable String trackId) {
8641f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            return false;
8651f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        }
8661f213914c45c23c653f721690da2ce0718e63139Dongwon Kang
8671f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        /**
868a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         * Processes a private command sent from the application to the TV input. This can be used
869a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         * to provide domain-specific features that are only known between certain TV inputs and
870a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         * their clients.
871a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         *
872a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         * @param action Name of the command to be performed. This <em>must</em> be a scoped name,
873a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         *            i.e. prefixed with a package name you own, so that different developers will
874a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         *            not create conflicting commands.
875a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         * @param data Any data to include with the command.
876a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         */
877c8b7356434f665c494504661a943323c0bbe702eJae Seo        public void onAppPrivateCommand(@NonNull String action, Bundle data) {
878a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        }
879a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo
880a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        /**
881465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * Called when the application requests to create an overlay view. Each session
8829a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         * implementation can override this method and return its own view.
8839a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         *
8849a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         * @return a view attached to the overlay window
8859a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         */
8869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public View onCreateOverlayView() {
8879a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            return null;
8889a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
8899a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
8909a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        /**
891a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * Called when the application requests to play a given recorded TV program.
892a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
893a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @param recordedProgramUri The URI of a recorded TV program.
894a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftResume()
895a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPause()
896a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSeekTo(long)
897a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
898a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetStartPosition()
899a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetCurrentPosition()
900a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
901a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public void onTimeShiftPlay(Uri recordedProgramUri) {
902a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
903a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
904a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
905465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * Called when the application requests to pause playback.
9066f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         *
907a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPlay(Uri)
908a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftResume()
909a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSeekTo(long)
910a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
911a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetStartPosition()
912a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetCurrentPosition()
9136f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
9146f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftPause() {
9156f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
9166f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
9176f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
918465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * Called when the application requests to resume playback.
9196f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         *
920a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPlay(Uri)
921a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPause()
922a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSeekTo(long)
923a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
924a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetStartPosition()
925a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetCurrentPosition()
9266f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
9276f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftResume() {
9286f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
9296f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
9306f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
931465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * Called when the application requests to seek to a specified time position. Normally, the
932465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * position is given within range between the start and the current time, inclusively. The
933465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * implementation is expected to seek to the nearest time position if the given position is
934465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * not in the range.
9356f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         *
936465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * @param timeMs The time position to seek to, in milliseconds since the epoch.
937a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPlay(Uri)
938a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftResume()
939a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPause()
940a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
941a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetStartPosition()
942a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetCurrentPosition()
9436f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
9446f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftSeekTo(long timeMs) {
9456f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
9466f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
9476f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
9483d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo         * Called when the application sets playback parameters containing the speed and audio mode.
9490610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
9503d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo         * <p>Once the playback parameters are set, the implementation should honor the current
9513d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo         * settings until the next tune request. Pause/resume/seek request does not reset the
9523d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo         * parameters previously set.
9536f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         *
9543d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo         * @param params The playback params.
955a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPlay(Uri)
956a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftResume()
957a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPause()
958a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSeekTo(long)
959a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetStartPosition()
960a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetCurrentPosition()
9616f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
9623d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo        public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
9636f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
9646f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
9656f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
9664e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * Returns the start position for time shifting, in milliseconds since the epoch.
967465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
968465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * moment.
9690610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
9704e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * <p>The start position for time shifting indicates the earliest possible time the user can
9714e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * seek to. Initially this is equivalent to the time when the implementation starts
9724e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * recording. Later it may be adjusted because there is insufficient space or the duration
9734e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * of recording is limited by the implementation. The application does not allow the user to
9744e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * seek to a position earlier than the start position.
9754e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         *
9764e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * <p>For playback of a recorded program initiated by {@link #onTimeShiftPlay(Uri)}, the
9774e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * start position is the time when playback starts. It does not change.
978465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         *
979a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPlay(Uri)
980a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftResume()
981a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPause()
982a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSeekTo(long)
983a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
984a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetCurrentPosition()
985465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         */
986465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        public long onTimeShiftGetStartPosition() {
987465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo            return TvInputManager.TIME_SHIFT_INVALID_TIME;
988465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        }
989465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo
990465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        /**
9914e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * Returns the current position for time shifting, in milliseconds since the epoch.
992465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
993465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * moment.
9946f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         *
9954e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * <p>The current position for time shifting is the same as the current position of
9964e3ded556100f674ccba0d0e40adcbd0d30f9b23Jae Seo         * playback. It should be equal to or greater than the start position reported by
9974a6d31a8f328be6163051f3d5d2da16ff551d128Conrad Chen         * {@link #onTimeShiftGetStartPosition()}. When playback is completed, the current position
9984a6d31a8f328be6163051f3d5d2da16ff551d128Conrad Chen         * should stay where the playback ends, in other words, the returned value of this mehtod
9994a6d31a8f328be6163051f3d5d2da16ff551d128Conrad Chen         * should be equal to the start position plus the duration of the program.
1000bb3e2674b5ad095d9c1e5e6069cd6b8ee1c1c11eDongwon Kang         *
1001a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPlay(Uri)
1002a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftResume()
1003a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftPause()
1004a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSeekTo(long)
1005a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
1006a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @see #onTimeShiftGetStartPosition()
10076f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
10086f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public long onTimeShiftGetCurrentPosition() {
10096f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            return TvInputManager.TIME_SHIFT_INVALID_TIME;
10106f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
10116f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
10126f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
10136a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Default implementation of {@link android.view.KeyEvent.Callback#onKeyDown(int, KeyEvent)
10146a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * KeyEvent.Callback.onKeyDown()}: always returns false (doesn't handle the event).
10150610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
10160610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Override this to intercept key down events before they are processed by the
10170610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * application. If you return true, the application will not process the event itself. If
10180610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * you return false, the normal application processing will occur as if the TV input had not
10190610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * seen the event at all.
10206a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *
10216a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param keyCode The value in event.getKeyCode().
10226a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param event Description of the key event.
10236a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @return If you handled the event, return {@code true}. If you want to allow the event to
10246a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *         be handled by the next receiver, return {@code false}.
10256a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
10266a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        @Override
10276a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        public boolean onKeyDown(int keyCode, KeyEvent event) {
10286a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            return false;
10296a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        }
10306a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
10316a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
10326a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Default implementation of
10336a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * {@link android.view.KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
10346a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle the event).
10350610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
10360610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Override this to intercept key long press events before they are processed by the
10376a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * application. If you return true, the application will not process the event itself. If
10386a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * you return false, the normal application processing will occur as if the TV input had not
10396a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * seen the event at all.
10406a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *
10416a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param keyCode The value in event.getKeyCode().
10426a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param event Description of the key event.
10436a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @return If you handled the event, return {@code true}. If you want to allow the event to
10446a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *         be handled by the next receiver, return {@code false}.
10456a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
10466a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        @Override
10476a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        public boolean onKeyLongPress(int keyCode, KeyEvent event) {
10486a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            return false;
10496a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        }
10506a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
10516a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
10526a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Default implementation of
10536a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * {@link android.view.KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
10546a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle the event).
10550610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
10560610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Override this to intercept special key multiple events before they are processed by
10570610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * the application. If you return true, the application will not itself process the event.
10580610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * If you return false, the normal application processing will occur as if the TV input had
10590610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * not seen the event at all.
10606a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *
10616a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param keyCode The value in event.getKeyCode().
10626a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param count The number of times the action was made.
10636a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param event Description of the key event.
10646a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @return If you handled the event, return {@code true}. If you want to allow the event to
10656a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *         be handled by the next receiver, return {@code false}.
10666a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
10676a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        @Override
10686a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
10696a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            return false;
10706a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        }
10716a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
10726a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
10736a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Default implementation of {@link android.view.KeyEvent.Callback#onKeyUp(int, KeyEvent)
10746a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * KeyEvent.Callback.onKeyUp()}: always returns false (doesn't handle the event).
10750610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
10760610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>Override this to intercept key up events before they are processed by the application.
10770610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * If you return true, the application will not itself process the event. If you return false,
10786a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * the normal application processing will occur as if the TV input had not seen the event at
10796a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * all.
10806a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *
10816a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param keyCode The value in event.getKeyCode().
10826a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param event Description of the key event.
10836a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @return If you handled the event, return {@code true}. If you want to allow the event to
10846a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *         be handled by the next receiver, return {@code false}.
10856a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
10866a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        @Override
10876a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        public boolean onKeyUp(int keyCode, KeyEvent event) {
10886a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            return false;
10896a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        }
10906a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
10916a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
10926a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Implement this method to handle touch screen motion events on the current input session.
10936a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *
10946a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param event The motion event being received.
10956a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @return If you handled the event, return {@code true}. If you want to allow the event to
10966a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *         be handled by the next receiver, return {@code false}.
10976a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @see View#onTouchEvent
10986a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
10996a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        public boolean onTouchEvent(MotionEvent event) {
11006a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            return false;
11016a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        }
11026a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
11036a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
11046a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Implement this method to handle trackball events on the current input session.
11056a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *
11066a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param event The motion event being received.
11076a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @return If you handled the event, return {@code true}. If you want to allow the event to
11086a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *         be handled by the next receiver, return {@code false}.
11096a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @see View#onTrackballEvent
11106a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
11116a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        public boolean onTrackballEvent(MotionEvent event) {
11126a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            return false;
11136a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        }
11146a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
11156a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
11166a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Implement this method to handle generic motion events on the current input session.
11176a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *
11186a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @param event The motion event being received.
11196a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @return If you handled the event, return {@code true}. If you want to allow the event to
11206a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         *         be handled by the next receiver, return {@code false}.
11216a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * @see View#onGenericMotionEvent
11226a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
11236a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        public boolean onGenericMotionEvent(MotionEvent event) {
11246a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            return false;
11256a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        }
11266a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
11276a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
11283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         * This method is called when the application would like to stop using the current input
11293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         * session.
11303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         */
1131674e96216d6a60f0d87d3a6a0d62f358a101532bYoungsang Cho        void release() {
1132674e96216d6a60f0d87d3a6a0d62f358a101532bYoungsang Cho            onRelease();
1133f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho            if (mSurface != null) {
1134f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                mSurface.release();
1135f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                mSurface = null;
1136f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho            }
11373eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang            synchronized(mLock) {
11383eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                mSessionCallback = null;
11393eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                mPendingActions.clear();
11403eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang            }
1141ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            // Removes the overlay view lastly so that any hanging on the main thread can be handled
1142ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            // in {@link #scheduleOverlayViewCleanup}.
1143ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            removeOverlayView(true);
1144465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo            mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable);
11453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
11473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        /**
114815c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee         * Calls {@link #onSetMain}.
11494c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee         */
115015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee        void setMain(boolean isMain) {
115115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            onSetMain(isMain);
11524c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        }
11534c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee
11544c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        /**
11556a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Calls {@link #onSetSurface}.
11563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         */
1157674e96216d6a60f0d87d3a6a0d62f358a101532bYoungsang Cho        void setSurface(Surface surface) {
1158674e96216d6a60f0d87d3a6a0d62f358a101532bYoungsang Cho            onSetSurface(surface);
1159f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho            if (mSurface != null) {
1160f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                mSurface.release();
1161f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho            }
1162f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho            mSurface = surface;
11633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // TODO: Handle failure.
11643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
11663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        /**
1167e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho         * Calls {@link #onSurfaceChanged}.
1168e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho         */
1169e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        void dispatchSurfaceChanged(int format, int width, int height) {
1170e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            if (DEBUG) {
1171e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width
1172e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                        + ", height=" + height + ")");
1173e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            }
1174e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            onSurfaceChanged(format, width, height);
1175e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        }
1176e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho
1177e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        /**
1178782f7345471072b630e58c7abd3579b0015273b1Jae Seo         * Calls {@link #onSetStreamVolume}.
11793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         */
11804b662d1b98e312792235c6718afd7c406270d1f1Dongwon Kang        void setStreamVolume(float volume) {
1181782f7345471072b630e58c7abd3579b0015273b1Jae Seo            onSetStreamVolume(volume);
11823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
11843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        /**
1185e12d810e99da093d3cf38f89c81e3e8d1e75b404Dongwon Kang         * Calls {@link #onTune(Uri, Bundle)}.
11863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo         */
11871a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim        void tune(Uri channelUri, Bundle params) {
11886f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
11891a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim            onTune(channelUri, params);
11903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // TODO: Handle failure.
11913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
11939a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        /**
11942c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         * Calls {@link #onSetCaptionEnabled}.
11952c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo         */
11962c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        void setCaptionEnabled(boolean enabled) {
11972c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            onSetCaptionEnabled(enabled);
11982c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        }
11992c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo
12002c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        /**
12011f213914c45c23c653f721690da2ce0718e63139Dongwon Kang         * Calls {@link #onSelectTrack}.
12021f213914c45c23c653f721690da2ce0718e63139Dongwon Kang         */
120310d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo        void selectTrack(int type, String trackId) {
120410d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo            onSelectTrack(type, trackId);
12051f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        }
12061f213914c45c23c653f721690da2ce0718e63139Dongwon Kang
12071f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        /**
120891a801d42f3acc35404da51ba26605093922503aJae Seo         * Calls {@link #onUnblockContent}.
1209903d6b72cd572665309633e925485464d08bb25aJaewan Kim         */
121091a801d42f3acc35404da51ba26605093922503aJae Seo        void unblockContent(String unblockedRating) {
121191a801d42f3acc35404da51ba26605093922503aJae Seo            onUnblockContent(TvContentRating.unflattenFromString(unblockedRating));
1212903d6b72cd572665309633e925485464d08bb25aJaewan Kim            // TODO: Handle failure.
1213903d6b72cd572665309633e925485464d08bb25aJaewan Kim        }
1214903d6b72cd572665309633e925485464d08bb25aJaewan Kim
1215903d6b72cd572665309633e925485464d08bb25aJaewan Kim        /**
1216a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         * Calls {@link #onAppPrivateCommand}.
1217a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo         */
1218a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        void appPrivateCommand(String action, Bundle data) {
1219a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            onAppPrivateCommand(action, data);
1220a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        }
1221a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo
1222a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        /**
12236a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach
12246a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * to the overlay window.
12259a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         *
1226465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * @param windowToken A window token of the application.
12279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         * @param frame A position of the overlay view.
12289a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         */
12299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        void createOverlayView(IBinder windowToken, Rect frame) {
1230ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            if (mOverlayViewContainer != null) {
1231ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                removeOverlayView(false);
12329a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
1233832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")");
12349a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowToken = windowToken;
12359a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mOverlayFrame = frame;
1236ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho            onOverlayViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
12379a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            if (!mOverlayViewEnabled) {
12389a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                return;
12399a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
12409a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mOverlayView = onCreateOverlayView();
12419a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            if (mOverlayView == null) {
12429a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                return;
12439a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
1244ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            if (mOverlayViewCleanUpTask != null) {
1245ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewCleanUpTask.cancel(true);
1246ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewCleanUpTask = null;
1247ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            }
1248ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            // Creates a container view to check hanging on the overlay view detaching.
1249ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            // Adding/removing the overlay view to/from the container make the view attach/detach
1250ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            // logic run on the main thread.
1251e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            mOverlayViewContainer = new FrameLayout(mContext.getApplicationContext());
1252ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            mOverlayViewContainer.addView(mOverlayView);
12539a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create
12549a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            // an overlay window above the media window but below the application window.
12559a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
12569a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            // We make the overlay view non-focusable and non-touchable so that
12579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            // the application that owns the window token can decide whether to consume or
12589a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            // dispatch the input events.
1259345af96677d5fbfc00f8f38a46ab7d57c1ff4cbbJae Seo            int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1260345af96677d5fbfc00f8f38a46ab7d57c1ff4cbbJae Seo                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
1261345af96677d5fbfc00f8f38a46ab7d57c1ff4cbbJae Seo                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
1262345af96677d5fbfc00f8f38a46ab7d57c1ff4cbbJae Seo            if (ActivityManager.isHighEndGfx()) {
1263345af96677d5fbfc00f8f38a46ab7d57c1ff4cbbJae Seo                flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1264345af96677d5fbfc00f8f38a46ab7d57c1ff4cbbJae Seo            }
12659a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowParams = new WindowManager.LayoutParams(
12669a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    frame.right - frame.left, frame.bottom - frame.top,
1267345af96677d5fbfc00f8f38a46ab7d57c1ff4cbbJae Seo                    frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT);
12689a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowParams.privateFlags |=
12699a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
12709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowParams.gravity = Gravity.START | Gravity.TOP;
12719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowParams.token = windowToken;
1272ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            mWindowManager.addView(mOverlayViewContainer, mWindowParams);
12739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
12749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
12759a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        /**
12769a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         * Relayouts the current overlay view.
12779a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         *
12789a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         * @param frame A new position of the overlay view.
12799a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         */
12809a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        void relayoutOverlayView(Rect frame) {
1281832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")");
1282ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho            if (mOverlayFrame == null || mOverlayFrame.width() != frame.width()
1283ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                    || mOverlayFrame.height() != frame.height()) {
1284ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                // Note: relayoutOverlayView is called whenever TvView's layout is changed
1285ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                // regardless of setOverlayViewEnabled.
1286ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho                onOverlayViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
1287ff04ae757a5542d2d5633e75b7adacc4fce1ce7eYoungsang Cho            }
12889a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mOverlayFrame = frame;
1289ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            if (!mOverlayViewEnabled || mOverlayViewContainer == null) {
12909a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                return;
12919a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
12929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowParams.x = frame.left;
12939a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowParams.y = frame.top;
12949a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowParams.width = frame.right - frame.left;
12959a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            mWindowParams.height = frame.bottom - frame.top;
1296ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            mWindowManager.updateViewLayout(mOverlayViewContainer, mWindowParams);
12979a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
12989a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
12999a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        /**
13009a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         * Removes the current overlay view.
13019a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho         */
13029a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        void removeOverlayView(boolean clearWindowToken) {
1303ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayViewContainer + ")");
13049a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            if (clearWindowToken) {
13059a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                mWindowToken = null;
13069a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                mOverlayFrame = null;
13079a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
1308ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            if (mOverlayViewContainer != null) {
1309ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                // Removes the overlay view from the view hierarchy in advance so that it can be
1310ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                // cleaned up in the {@link OverlayViewCleanUpTask} if the remove process is
1311ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                // hanging.
1312ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewContainer.removeView(mOverlayView);
13139a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                mOverlayView = null;
1314ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mWindowManager.removeView(mOverlayViewContainer);
1315ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewContainer = null;
13169a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                mWindowParams = null;
13179a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
13189a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
13196a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
13206a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        /**
1321a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * Calls {@link #onTimeShiftPlay(Uri)}.
1322a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1323a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        void timeShiftPlay(Uri recordedProgramUri) {
1324a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            mCurrentPositionMs = 0;
1325a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            onTimeShiftPlay(recordedProgramUri);
1326a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1327a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1328a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
13296f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         * Calls {@link #onTimeShiftPause}.
13306f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
13316f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        void timeShiftPause() {
13326f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            onTimeShiftPause();
13336f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
13346f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
13356f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
13366f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         * Calls {@link #onTimeShiftResume}.
13376f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
13386f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        void timeShiftResume() {
13396f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            onTimeShiftResume();
13406f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
13416f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
13426f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
13436f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         * Calls {@link #onTimeShiftSeekTo}.
13446f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
13456f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        void timeShiftSeekTo(long timeMs) {
13466f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            onTimeShiftSeekTo(timeMs);
13476f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
13486f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
13496f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
13503d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo         * Calls {@link #onTimeShiftSetPlaybackParams}.
13516f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
13523d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo        void timeShiftSetPlaybackParams(PlaybackParams params) {
13533d04b7693428dd887c93e2ac7f4e90fd0cc87859Jae Seo            onTimeShiftSetPlaybackParams(params);
13546f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
13556f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
13566f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
1357465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * Enable/disable position tracking.
1358465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         *
1359465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo         * @param enable {@code true} to enable tracking, {@code false} otherwise.
13606f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang         */
1361465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        void timeShiftEnablePositionTracking(boolean enable) {
1362465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo            if (enable) {
1363465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                mHandler.post(mTimeShiftPositionTrackingRunnable);
13646f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } else {
1365465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable);
1366465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
13676f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
13686f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
13696f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
13706f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
13716f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        /**
1372ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang         * Schedules a task which checks whether the overlay view is detached and kills the process
1373ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang         * if it is not. Note that this method is expected to be called in a non-main thread.
1374ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang         */
1375ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang        void scheduleOverlayViewCleanup() {
1376ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            View overlayViewParent = mOverlayViewContainer;
1377ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            if (overlayViewParent != null) {
1378ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewCleanUpTask = new OverlayViewCleanUpTask();
1379ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
1380ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                        overlayViewParent);
1381ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            }
1382ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang        }
1383ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang
1384ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang        /**
13856a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         * Takes care of dispatching incoming input events and tells whether the event was handled.
13866a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo         */
13876a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
13886a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
138944fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            boolean isNavigationKey = false;
139053aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho            boolean skipDispatchToOverlayView = false;
13916a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            if (event instanceof KeyEvent) {
139244fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                KeyEvent keyEvent = (KeyEvent) event;
139344fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                if (keyEvent.dispatch(this, mDispatcherState, this)) {
1394782f7345471072b630e58c7abd3579b0015273b1Jae Seo                    return TvInputManager.Session.DISPATCH_HANDLED;
13956a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                }
139653aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho                isNavigationKey = isNavigationKey(keyEvent.getKeyCode());
139753aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho                // When media keys and KEYCODE_MEDIA_AUDIO_TRACK are dispatched to ViewRootImpl,
1398465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                // ViewRootImpl always consumes the keys. In this case, the application loses
139953aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho                // a chance to handle media keys. Therefore, media keys are not dispatched to
140053aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho                // ViewRootImpl.
140153aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho                skipDispatchToOverlayView = KeyEvent.isMediaKey(keyEvent.getKeyCode())
140253aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho                        || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK;
14036a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            } else if (event instanceof MotionEvent) {
14046a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                MotionEvent motionEvent = (MotionEvent) event;
14056a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                final int source = motionEvent.getSource();
14066a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                if (motionEvent.isTouchEvent()) {
14076a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    if (onTouchEvent(motionEvent)) {
1408782f7345471072b630e58c7abd3579b0015273b1Jae Seo                        return TvInputManager.Session.DISPATCH_HANDLED;
14096a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    }
14106a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
14116a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    if (onTrackballEvent(motionEvent)) {
1412782f7345471072b630e58c7abd3579b0015273b1Jae Seo                        return TvInputManager.Session.DISPATCH_HANDLED;
14136a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    }
14146a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                } else {
14156a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    if (onGenericMotionEvent(motionEvent)) {
1416782f7345471072b630e58c7abd3579b0015273b1Jae Seo                        return TvInputManager.Session.DISPATCH_HANDLED;
14176a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    }
14186a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                }
14196a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            }
142053aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho            if (mOverlayViewContainer == null || !mOverlayViewContainer.isAttachedToWindow()
142153aa5744c9a233e54b8f8b8389a86f99abcc4244Youngsang Cho                    || skipDispatchToOverlayView) {
1422782f7345471072b630e58c7abd3579b0015273b1Jae Seo                return TvInputManager.Session.DISPATCH_NOT_HANDLED;
14236a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            }
1424ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            if (!mOverlayViewContainer.hasWindowFocus()) {
1425ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewContainer.getViewRootImpl().windowFocusChanged(true, true);
14266a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            }
1427ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            if (isNavigationKey && mOverlayViewContainer.hasFocusable()) {
142844fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                // If mOverlayView has focusable views, navigation key events should be always
142944fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                // handled. If not, it can make the application UI navigation messed up.
143044fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                // For example, in the case that the left-most view is focused, a left key event
143144fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                // will not be handled in ViewRootImpl. Then, the left key event will be handled in
143244fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                // the application during the UI navigation of the TV input.
1433ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewContainer.getViewRootImpl().dispatchInputEvent(event);
143444fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                return TvInputManager.Session.DISPATCH_HANDLED;
143544fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            } else {
1436ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                mOverlayViewContainer.getViewRootImpl().dispatchInputEvent(event, receiver);
143744fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                return TvInputManager.Session.DISPATCH_IN_PROGRESS;
143844fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            }
14396a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        }
1440832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
14413eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang        private void initialize(ITvInputSessionCallback callback) {
14423eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang            synchronized(mLock) {
14433eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                mSessionCallback = callback;
14443eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                for (Runnable runnable : mPendingActions) {
14453eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    runnable.run();
14463eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                }
14473eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                mPendingActions.clear();
14483eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang            }
1449832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho        }
14509044be13dc0b625f10c459574abdd22603f86d50Youngsang Cho
1451a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        private void executeOrPostRunnableOnMainThread(Runnable action) {
14523eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang            synchronized(mLock) {
14533eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                if (mSessionCallback == null) {
14543eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    // The session is not initialized yet.
14553eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    mPendingActions.add(action);
14563eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                } else {
14573eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    if (mHandler.getLooper().isCurrentThread()) {
14583eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                        action.run();
14593eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    } else {
14603eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                        // Posts the runnable if this is not called from the main thread
14613eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                        mHandler.post(action);
14623eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    }
14633eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                }
14649044be13dc0b625f10c459574abdd22603f86d50Youngsang Cho            }
14659044be13dc0b625f10c459574abdd22603f86d50Youngsang Cho        }
1466ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang
1467465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        private final class TimeShiftPositionTrackingRunnable implements Runnable {
14686f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            @Override
14696f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            public void run() {
1470465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                long startPositionMs = onTimeShiftGetStartPosition();
147173d86e34bb2ce73c88f1a3496b71fe34113d1296Conrad Chen                if (mStartPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME
147273d86e34bb2ce73c88f1a3496b71fe34113d1296Conrad Chen                        || mStartPositionMs != startPositionMs) {
1473465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                    mStartPositionMs = startPositionMs;
1474465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                    notifyTimeShiftStartPositionChanged(startPositionMs);
1475465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                }
1476465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                long currentPositionMs = onTimeShiftGetCurrentPosition();
1477bb3e2674b5ad095d9c1e5e6069cd6b8ee1c1c11eDongwon Kang                if (currentPositionMs < mStartPositionMs) {
1478bb3e2674b5ad095d9c1e5e6069cd6b8ee1c1c11eDongwon Kang                    Log.w(TAG, "Current position (" + currentPositionMs + ") cannot be earlier than"
1479bb3e2674b5ad095d9c1e5e6069cd6b8ee1c1c11eDongwon Kang                            + " start position (" + mStartPositionMs + "). Reset to the start "
1480bb3e2674b5ad095d9c1e5e6069cd6b8ee1c1c11eDongwon Kang                            + "position.");
1481bb3e2674b5ad095d9c1e5e6069cd6b8ee1c1c11eDongwon Kang                    currentPositionMs = mStartPositionMs;
1482bb3e2674b5ad095d9c1e5e6069cd6b8ee1c1c11eDongwon Kang                }
148373d86e34bb2ce73c88f1a3496b71fe34113d1296Conrad Chen                if (mCurrentPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME
148473d86e34bb2ce73c88f1a3496b71fe34113d1296Conrad Chen                        || mCurrentPositionMs != currentPositionMs) {
1485465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                    mCurrentPositionMs = currentPositionMs;
1486465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                    notifyTimeShiftCurrentPositionChanged(currentPositionMs);
14876f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
1488465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable);
1489465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                mHandler.postDelayed(mTimeShiftPositionTrackingRunnable,
14906f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        POSITION_UPDATE_INTERVAL_MS);
14916f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
14926f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
1493e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho    }
14946f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
1495e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho    private static final class OverlayViewCleanUpTask extends AsyncTask<View, Void, Void> {
1496e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho        @Override
1497e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho        protected Void doInBackground(View... views) {
1498e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            View overlayViewParent = views[0];
1499e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            try {
1500e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho                Thread.sleep(DETACH_OVERLAY_VIEW_TIMEOUT_MS);
1501e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            } catch (InterruptedException e) {
1502e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho                return null;
1503e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            }
1504e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            if (isCancelled()) {
1505ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang                return null;
1506ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang            }
1507e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            if (overlayViewParent.isAttachedToWindow()) {
1508e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho                Log.e(TAG, "Time out on releasing overlay view. Killing "
1509e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho                        + overlayViewParent.getContext().getPackageName());
1510e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho                Process.killProcess(Process.myPid());
1511e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            }
1512e2cd56cc8aeaa317f31fd7732587d1a22ef1f3d5Youngsang Cho            return null;
1513ce34c6d308629c214ab9b7963755eb60cac03c9dDongwon Kang        }
15143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
15153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1516bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang    /**
1517a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     * Base class for derived classes to implement to provide a TV input recording session.
1518a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo     */
1519a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo    public abstract static class RecordingSession {
1520a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        final Handler mHandler;
1521a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1522a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        private final Object mLock = new Object();
1523a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        // @GuardedBy("mLock")
1524a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        private ITvInputSessionCallback mSessionCallback;
1525a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        // @GuardedBy("mLock")
1526a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        private final List<Runnable> mPendingActions = new ArrayList<>();
1527a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1528a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1529e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Creates a new RecordingSession.
1530a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1531a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @param context The context of the application
1532a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1533a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public RecordingSession(Context context) {
1534a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            mHandler = new Handler(context.getMainLooper());
1535a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1536a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1537a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1538e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Informs the application that this recording session has been tuned to the given channel
1539e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * and is ready to start recording.
1540e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         *
1541e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * <p>Upon receiving a call to {@link #onTune(Uri)}, the session is expected to tune to the
1542e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * passed channel and call this method to indicate that it is now available for immediate
1543e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * recording. When {@link #onStartRecording(Uri)} is called, recording must start with
1544e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * minimal delay.
1545b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang         *
1546b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang         * @param channelUri The URI of a channel.
1547a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1548b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang        public void notifyTuned(Uri channelUri) {
1549a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
1550a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
1551a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @Override
1552a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                public void run() {
1553a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    try {
1554e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo                        if (DEBUG) Log.d(TAG, "notifyTuned");
1555a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        if (mSessionCallback != null) {
1556b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang                            mSessionCallback.onTuned(channelUri);
1557a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        }
1558a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } catch (RemoteException e) {
1559e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo                        Log.w(TAG, "error in notifyTuned", e);
1560a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
1561a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1562a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            });
1563a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1564a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1565a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1566e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Informs the application that this recording session has stopped recording and created a
1567e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly
1568e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * recorded program.
1569a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
157025c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * <p>The recording session must call this method in response to {@link #onStopRecording()}.
157125c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * The session may call it even before receiving a call to {@link #onStopRecording()} if a
157225c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * partially recorded program is available when there is an error.
157325c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         *
1574e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * @param recordedProgramUri The URI of the newly recorded program.
1575a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1576a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public void notifyRecordingStopped(final Uri recordedProgramUri) {
1577a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
1578a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
1579a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @Override
1580a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                public void run() {
1581a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    try {
1582a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        if (DEBUG) Log.d(TAG, "notifyRecordingStopped");
1583a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        if (mSessionCallback != null) {
1584a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                            mSessionCallback.onRecordingStopped(recordedProgramUri);
1585a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        }
1586a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } catch (RemoteException e) {
1587a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        Log.w(TAG, "error in notifyRecordingStopped", e);
1588a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
1589a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1590a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            });
1591a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1592a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1593a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
159425c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * Informs the application that there is an error and this recording session is no longer
159525c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * able to start or continue recording. It may be called at any time after the recording
159625c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * session is created until {@link #onRelease()} is called.
159725c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         *
159825c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * <p>The application may release the current session upon receiving the error code through
159925c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * {@link TvRecordingClient.RecordingCallback#onError(int)}. The session may call
160025c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * {@link #notifyRecordingStopped(Uri)} if a partially recorded but still playable program
160125c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo         * is available, before calling this method.
1602a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1603a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @param error The error code. Should be one of the followings.
1604a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * <ul>
1605a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
1606a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
1607a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
1608a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * </ul>
1609a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1610150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang        public void notifyError(@TvInputManager.RecordingError int error) {
1611150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang            if (error < TvInputManager.RECORDING_ERROR_START
1612150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang                    || error > TvInputManager.RECORDING_ERROR_END) {
1613150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang                Log.w(TAG, "notifyError - invalid error code (" + error
1614150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang                        + ") is changed to RECORDING_ERROR_UNKNOWN.");
1615150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang                error = TvInputManager.RECORDING_ERROR_UNKNOWN;
1616150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang            }
1617150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang            final int validError = error;
1618a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
1619a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
1620a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @Override
1621a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                public void run() {
1622a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    try {
1623a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        if (DEBUG) Log.d(TAG, "notifyError");
1624a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        if (mSessionCallback != null) {
1625150923ac6adf3f618e9e1ac9d4d600a9c66bd812Dongwon Kang                            mSessionCallback.onError(validError);
1626a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        }
1627a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } catch (RemoteException e) {
1628a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        Log.w(TAG, "error in notifyError", e);
1629a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
1630a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1631a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            });
1632a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1633a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1634a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1635a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * Dispatches an event to the application using this recording session.
1636a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1637a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @param eventType The type of the event.
1638a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @param eventArgs Optional arguments of the event.
1639a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @hide
1640a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1641a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        @SystemApi
1642a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) {
1643a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            Preconditions.checkNotNull(eventType);
1644a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            executeOrPostRunnableOnMainThread(new Runnable() {
1645a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @MainThread
1646a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                @Override
1647a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                public void run() {
1648a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    try {
1649a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")");
1650a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        if (mSessionCallback != null) {
1651a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                            mSessionCallback.onSessionEvent(eventType, eventArgs);
1652a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        }
1653a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } catch (RemoteException e) {
1654a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        Log.w(TAG, "error in sending event (event=" + eventType + ")", e);
1655a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
1656a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1657a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            });
1658a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1659a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1660a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1661e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Called when the application requests to tune to a given channel for TV program recording.
1662a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1663e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * <p>The application may call this method before starting or after stopping recording, but
1664e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * not during recording.
1665e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         *
1666b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang         * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or
1667e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * {@link #notifyError(int)} otherwise.
1668e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         *
1669e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * @param channelUri The URI of a channel.
1670a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1671e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo        public abstract void onTune(Uri channelUri);
1672a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1673a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1674031d7e838d9f026248e9e54a4aa025d2605bce78Dongwon Kang         * Called when the application requests to tune to a given channel for TV program recording.
1675031d7e838d9f026248e9e54a4aa025d2605bce78Dongwon Kang         * Override this method in order to handle domain-specific features that are only known
1676031d7e838d9f026248e9e54a4aa025d2605bce78Dongwon Kang         * between certain TV inputs and their clients.
1677a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1678e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * <p>The application may call this method before starting or after stopping recording, but
1679031d7e838d9f026248e9e54a4aa025d2605bce78Dongwon Kang         * not during recording. The default implementation calls {@link #onTune(Uri)}.
1680e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         *
1681d48d029da43babf265fccbf5d84a06b4b275f72cJiabin         * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or
1682e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * {@link #notifyError(int)} otherwise.
1683e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         *
1684e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * @param channelUri The URI of a channel.
1685d48d029da43babf265fccbf5d84a06b4b275f72cJiabin         * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
1686d48d029da43babf265fccbf5d84a06b4b275f72cJiabin         *            name, i.e. prefixed with a package name you own, so that different developers
1687d48d029da43babf265fccbf5d84a06b4b275f72cJiabin         *            will not create conflicting keys.
1688a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1689e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo        public void onTune(Uri channelUri, Bundle params) {
1690e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo            onTune(channelUri);
1691a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1692a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1693a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1694e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Called when the application requests to start TV program recording. Recording must start
1695e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * immediately when this method is called.
1696a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1697b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang         * <p>The application may supply the URI for a TV program for filling in program specific
1698b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang         * data fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
16990cb5244e52590214ddc16dd5fc1030b5baf04726Dongwon Kang         * A non-null {@code programUri} implies the started recording should be of that specific
17000cb5244e52590214ddc16dd5fc1030b5baf04726Dongwon Kang         * program, whereas null {@code programUri} does not impose such a requirement and the
17014eee6a73e476cd2d82a69f3a535628901047f140Jae Seo         * recording can span across multiple TV programs. In either case, the application must call
1702cd502cf7150f0fb426f7f808c7d407ce89705239Jae Seo         * {@link TvRecordingClient#stopRecording()} to stop the recording.
17034eee6a73e476cd2d82a69f3a535628901047f140Jae Seo         *
1704e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * <p>The session must call {@link #notifyError(int)} if the start request cannot be
1705e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * fulfilled.
17064eee6a73e476cd2d82a69f3a535628901047f140Jae Seo         *
1707b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang         * @param programUri The URI for the TV program to record, built by
17084eee6a73e476cd2d82a69f3a535628901047f140Jae Seo         *            {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
1709a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1710b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang        public abstract void onStartRecording(@Nullable Uri programUri);
1711a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1712a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1713e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Called when the application requests to stop TV program recording. Recording must stop
1714e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * immediately when this method is called.
1715e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         *
1716e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * <p>The session must create a new data entry in the
1717e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * {@link android.media.tv.TvContract.RecordedPrograms} table that describes the newly
1718e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * recorded program and call {@link #notifyRecordingStopped(Uri)} with the URI to that
1719e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * entry.
1720e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * If the stop request cannot be fulfilled, the session must call {@link #notifyError(int)}.
1721a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1722a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1723a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public abstract void onStopRecording();
1724a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1725e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo
1726e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo        /**
1727e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Called when the application requests to release all the resources held by this recording
1728e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * session.
1729e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         */
1730e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo        public abstract void onRelease();
1731e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo
1732a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1733a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * Processes a private command sent from the application to the TV input. This can be used
1734a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * to provide domain-specific features that are only known between certain TV inputs and
1735a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * their clients.
1736a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1737a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @param action Name of the command to be performed. This <em>must</em> be a scoped name,
1738a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *            i.e. prefixed with a package name you own, so that different developers will
1739a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *            not create conflicting commands.
1740a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * @param data Any data to include with the command.
1741a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1742a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public void onAppPrivateCommand(@NonNull String action, Bundle data) {
1743a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1744a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1745a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1746e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Calls {@link #onTune(Uri, Bundle)}.
1747a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1748a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1749e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo        void tune(Uri channelUri, Bundle params) {
1750e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo            onTune(channelUri, params);
1751a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1752a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1753a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1754e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo         * Calls {@link #onRelease()}.
1755a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1756a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1757e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo        void release() {
1758e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo            onRelease();
1759a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1760a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1761a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
17624eee6a73e476cd2d82a69f3a535628901047f140Jae Seo         * Calls {@link #onStartRecording(Uri)}.
1763a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1764a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
17650cb5244e52590214ddc16dd5fc1030b5baf04726Dongwon Kang        void startRecording(@Nullable  Uri programUri) {
17660cb5244e52590214ddc16dd5fc1030b5baf04726Dongwon Kang            onStartRecording(programUri);
1767a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1768a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1769a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1770a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * Calls {@link #onStopRecording()}.
1771a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         *
1772a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1773a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        void stopRecording() {
1774a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            onStopRecording();
1775a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1776a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1777a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        /**
1778a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         * Calls {@link #onAppPrivateCommand(String, Bundle)}.
1779a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo         */
1780a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        void appPrivateCommand(String action, Bundle data) {
1781a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            onAppPrivateCommand(action, data);
1782a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1783a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1784a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        private void initialize(ITvInputSessionCallback callback) {
1785a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            synchronized(mLock) {
1786a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                mSessionCallback = callback;
1787a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                for (Runnable runnable : mPendingActions) {
1788a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    runnable.run();
1789a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1790a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                mPendingActions.clear();
1791a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
1792a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1793a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1794a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        private void executeOrPostRunnableOnMainThread(Runnable action) {
1795a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            synchronized(mLock) {
1796a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                if (mSessionCallback == null) {
1797a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    // The session is not initialized yet.
1798a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    mPendingActions.add(action);
1799a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                } else {
1800a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    if (mHandler.getLooper().isCurrentThread()) {
1801a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        action.run();
1802a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } else {
1803a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        // Posts the runnable if this is not called from the main thread
1804a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        mHandler.post(action);
1805a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
1806a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1807a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
1808a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1809a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo    }
1810a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1811a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo    /**
1812bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang     * Base class for a TV input session which represents an external device connected to a
181349c1b69a568ce58c940e5a2b0641a2c50798cbe2Dongwon Kang     * hardware TV input.
18140610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     *
18150610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     * <p>This class is for an input which provides channels for the external set-top box to the
181649c1b69a568ce58c940e5a2b0641a2c50798cbe2Dongwon Kang     * application. Once a TV input returns an implementation of this class on
181749c1b69a568ce58c940e5a2b0641a2c50798cbe2Dongwon Kang     * {@link #onCreateSession(String)}, the framework will create a separate session for
181849c1b69a568ce58c940e5a2b0641a2c50798cbe2Dongwon Kang     * a hardware TV Input (e.g. HDMI 1) and forward the application's surface to the session so
181949c1b69a568ce58c940e5a2b0641a2c50798cbe2Dongwon Kang     * that the user can see the screen of the hardware TV Input when she tunes to a channel from
182049c1b69a568ce58c940e5a2b0641a2c50798cbe2Dongwon Kang     * this TV input. The implementation of this class is expected to change the channel of the
1821e12d810e99da093d3cf38f89c81e3e8d1e75b404Dongwon Kang     * external set-top box via a proprietary protocol when {@link HardwareSession#onTune} is
182249c1b69a568ce58c940e5a2b0641a2c50798cbe2Dongwon Kang     * requested by the application.
18230610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     *
18240610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     * <p>Note that this class is not for inputs for internal hardware like built-in tuner and HDMI
18250610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     * 1.
18260610e12733875a267f59d87a2a68aebbf486066eDongwon Kang     *
1827bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang     * @see #onCreateSession(String)
1828bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang     */
182966b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang    public abstract static class HardwareSession extends Session {
183066b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang
183166b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang        /**
183266b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang         * Creates a new HardwareSession.
183366b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang         *
183466b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang         * @param context The context of the application
183566b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang         */
183666b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang        public HardwareSession(Context context) {
183766b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang            super(context);
183866b9e91801ba8867514fbcf0ef8f03a2f4fb8798Dongwon Kang        }
1839bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
1840bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        private TvInputManager.Session mHardwareSession;
1841bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        private ITvInputSession mProxySession;
1842bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        private ITvInputSessionCallback mProxySessionCallback;
1843c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang        private Handler mServiceHandler;
1844bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
1845bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        /**
1846bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * Returns the hardware TV input ID the external device is connected to.
18470610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         *
18480610e12733875a267f59d87a2a68aebbf486066eDongwon Kang         * <p>TV input is expected to provide {@link android.R.attr#setupActivity} so that
1849bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * the application can launch it before using this TV input. The setup activity may let
1850bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * the user select the hardware TV input to which the external device is connected. The ID
1851bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * of the selected one should be stored in the TV input so that it can be returned here.
1852bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         */
1853bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        public abstract String getHardwareInputId();
1854bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
1855bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        private final TvInputManager.SessionCallback mHardwareSessionCallback =
1856bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                new TvInputManager.SessionCallback() {
1857bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            @Override
1858bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            public void onSessionCreated(TvInputManager.Session session) {
1859bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                mHardwareSession = session;
1860bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                SomeArgs args = SomeArgs.obtain();
1861bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                if (session != null) {
18623eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    args.arg1 = HardwareSession.this;
18633eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    args.arg2 = mProxySession;
18643eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    args.arg3 = mProxySessionCallback;
18653eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    args.arg4 = session.getToken();
1866731843e424a0d7091eb42d579aab6a66a74b92caDongwon Kang                    session.tune(TvContract.buildChannelUriForPassthroughInput(
1867731843e424a0d7091eb42d579aab6a66a74b92caDongwon Kang                            getHardwareInputId()));
1868bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                } else {
1869bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    args.arg1 = null;
18703eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    args.arg2 = null;
18713eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    args.arg3 = mProxySessionCallback;
18723eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    args.arg4 = null;
1873bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    onRelease();
1874bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                }
1875c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, args)
1876bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        .sendToTarget();
1877bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            }
1878bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
1879bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            @Override
1880bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            public void onVideoAvailable(final TvInputManager.Session session) {
1881bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                if (mHardwareSession == session) {
1882bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    onHardwareVideoAvailable();
1883bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                }
1884bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            }
1885bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
1886bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            @Override
1887bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            public void onVideoUnavailable(final TvInputManager.Session session,
1888bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    final int reason) {
1889bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                if (mHardwareSession == session) {
1890bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    onHardwareVideoUnavailable(reason);
1891bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                }
1892bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            }
1893bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        };
1894bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
1895bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        /**
1896bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * This method will not be called in {@link HardwareSession}. Framework will
1897bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * forward the application's surface to the hardware TV input.
1898bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         */
1899bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        @Override
1900bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        public final boolean onSetSurface(Surface surface) {
1901bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            Log.e(TAG, "onSetSurface() should not be called in HardwareProxySession.");
1902bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            return false;
1903bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        }
1904bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
1905bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        /**
1906bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * Called when the underlying hardware TV input session calls
1907bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * {@link TvInputService.Session#notifyVideoAvailable()}.
1908bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         */
1909bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        public void onHardwareVideoAvailable() { }
1910bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
1911bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        /**
1912bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * Called when the underlying hardware TV input session calls
1913bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * {@link TvInputService.Session#notifyVideoUnavailable(int)}.
1914bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         *
1915bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * @param reason The reason that the hardware TV input stopped the playback:
1916bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * <ul>
1917bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
1918bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
1919bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
1920bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
1921ff1f29e1b112e68d16908b1a89225315089f8e50Dongwon Kang         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY}
1922bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         * </ul>
1923bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang         */
1924bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        public void onHardwareVideoUnavailable(int reason) { }
192578364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang
192678364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang        @Override
192778364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang        void release() {
192878364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang            if (mHardwareSession != null) {
192978364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang                mHardwareSession.release();
193078364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang                mHardwareSession = null;
193178364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang            }
193278364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang            super.release();
193378364ddc047100306f0ccacc8fdad043d9a43653Dongwon Kang        }
1934bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang    }
1935bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang
193644fbbca35459ee756a531661eba53ec33419790aYoungsang Cho    /** @hide */
193744fbbca35459ee756a531661eba53ec33419790aYoungsang Cho    public static boolean isNavigationKey(int keyCode) {
193844fbbca35459ee756a531661eba53ec33419790aYoungsang Cho        switch (keyCode) {
193944fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_DPAD_LEFT:
194044fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_DPAD_RIGHT:
194144fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_DPAD_UP:
194244fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_DPAD_DOWN:
194344fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_DPAD_CENTER:
194444fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_PAGE_UP:
194544fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_PAGE_DOWN:
194644fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_MOVE_HOME:
194744fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_MOVE_END:
194844fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_TAB:
194944fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_SPACE:
195044fbbca35459ee756a531661eba53ec33419790aYoungsang Cho            case KeyEvent.KEYCODE_ENTER:
195144fbbca35459ee756a531661eba53ec33419790aYoungsang Cho                return true;
195244fbbca35459ee756a531661eba53ec33419790aYoungsang Cho        }
195344fbbca35459ee756a531661eba53ec33419790aYoungsang Cho        return false;
195444fbbca35459ee756a531661eba53ec33419790aYoungsang Cho    }
195544fbbca35459ee756a531661eba53ec33419790aYoungsang Cho
19562c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo    @SuppressLint("HandlerLeak")
19573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceHandler extends Handler {
19583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private static final int DO_CREATE_SESSION = 1;
1959bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        private static final int DO_NOTIFY_SESSION_CREATED = 2;
1960a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        private static final int DO_CREATE_RECORDING_SESSION = 3;
19611abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        private static final int DO_ADD_HARDWARE_INPUT = 4;
19621abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        private static final int DO_REMOVE_HARDWARE_INPUT = 5;
19631abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        private static final int DO_ADD_HDMI_INPUT = 6;
19641abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        private static final int DO_REMOVE_HDMI_INPUT = 7;
19654f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
19661abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        private void broadcastAddHardwareInput(int deviceId, TvInputInfo inputInfo) {
19674f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            int n = mCallbacks.beginBroadcast();
19684f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            for (int i = 0; i < n; ++i) {
19694f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                try {
19701abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                    mCallbacks.getBroadcastItem(i).addHardwareInput(deviceId, inputInfo);
19714f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                } catch (RemoteException e) {
19721abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                    Log.e(TAG, "error in broadcastAddHardwareInput", e);
19734f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                }
19744f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
19754f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mCallbacks.finishBroadcast();
19764f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
1977187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
19781abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        private void broadcastAddHdmiInput(int id, TvInputInfo inputInfo) {
1979187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            int n = mCallbacks.beginBroadcast();
1980187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            for (int i = 0; i < n; ++i) {
1981187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                try {
19821abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                    mCallbacks.getBroadcastItem(i).addHdmiInput(id, inputInfo);
1983187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                } catch (RemoteException e) {
19841abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                    Log.e(TAG, "error in broadcastAddHdmiInput", e);
1985187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1986187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
1987187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mCallbacks.finishBroadcast();
1988187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
1989187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
19901abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        private void broadcastRemoveHardwareInput(String inputId) {
1991187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            int n = mCallbacks.beginBroadcast();
1992187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            for (int i = 0; i < n; ++i) {
1993187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                try {
19941abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                    mCallbacks.getBroadcastItem(i).removeHardwareInput(inputId);
1995187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                } catch (RemoteException e) {
19961abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                    Log.e(TAG, "error in broadcastRemoveHardwareInput", e);
199779124a717c09f12c74d587d3977bf33ca37e6420Terry Heo                }
199879124a717c09f12c74d587d3977bf33ca37e6420Terry Heo            }
199979124a717c09f12c74d587d3977bf33ca37e6420Terry Heo            mCallbacks.finishBroadcast();
200079124a717c09f12c74d587d3977bf33ca37e6420Terry Heo        }
200179124a717c09f12c74d587d3977bf33ca37e6420Terry Heo
20023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
20033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public final void handleMessage(Message msg) {
20043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            switch (msg.what) {
20053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                case DO_CREATE_SESSION: {
20066a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
20076a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    InputChannel channel = (InputChannel) args.arg1;
20086a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
2009187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    String inputId = (String) args.arg3;
2010bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    args.recycle();
2011c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                    Session sessionImpl = onCreateSession(inputId);
2012bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    if (sessionImpl == null) {
2013bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        try {
20143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            // Failed to create a session.
2015bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            cb.onSessionCreated(null, null);
2016bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        } catch (RemoteException e) {
2017465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                            Log.e(TAG, "error in onSessionCreated", e);
2018bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
2019c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                        return;
2020c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                    }
2021c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                    ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
2022c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                            sessionImpl, channel);
2023c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                    if (sessionImpl instanceof HardwareSession) {
2024c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                        HardwareSession proxySession =
2025c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                                ((HardwareSession) sessionImpl);
20266e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                        String hardwareInputId = proxySession.getHardwareInputId();
20276e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                        if (TextUtils.isEmpty(hardwareInputId) ||
20286e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                                !isPassthroughInput(hardwareInputId)) {
20296e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                            if (TextUtils.isEmpty(hardwareInputId)) {
2030911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang                                Log.w(TAG, "Hardware input id is not setup yet.");
2031911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang                            } else {
20326e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                                Log.w(TAG, "Invalid hardware input id : " + hardwareInputId);
2033911d0827ca67d61c141ab91e75816da94b3f414eDongwon Kang                            }
2034c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                            sessionImpl.onRelease();
2035c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                            try {
2036c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                                cb.onSessionCreated(null, null);
2037c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                            } catch (RemoteException e) {
2038465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                                Log.e(TAG, "error in onSessionCreated", e);
2039bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            }
2040c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                            return;
20413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
2042c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                        proxySession.mProxySession = stub;
2043c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                        proxySession.mProxySessionCallback = cb;
2044c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                        proxySession.mServiceHandler = mServiceHandler;
2045c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                        TvInputManager manager = (TvInputManager) getSystemService(
2046c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                                Context.TV_INPUT_SERVICE);
20476e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                        manager.createSession(hardwareInputId,
2048c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                                proxySession.mHardwareSessionCallback, mServiceHandler);
2049c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                    } else {
2050c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                        SomeArgs someArgs = SomeArgs.obtain();
20513eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                        someArgs.arg1 = sessionImpl;
20523eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                        someArgs.arg2 = stub;
20533eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                        someArgs.arg3 = cb;
20543eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                        someArgs.arg4 = null;
2055c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                        mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED,
2056c566ca54b10fca266216555c3b6fb56a5ac63343Dongwon Kang                                someArgs).sendToTarget();
2057bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    }
2058bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    return;
2059bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                }
2060bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                case DO_NOTIFY_SESSION_CREATED: {
2061bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    SomeArgs args = (SomeArgs) msg.obj;
20623eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    Session sessionImpl = (Session) args.arg1;
20633eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    ITvInputSession stub = (ITvInputSession) args.arg2;
20643eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg3;
20653eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    IBinder hardwareSessionToken = (IBinder) args.arg4;
2066bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    try {
2067bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        cb.onSessionCreated(stub, hardwareSessionToken);
20683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
2069465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Log.e(TAG, "error in onSessionCreated", e);
20703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
20713eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    if (sessionImpl != null) {
20723eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                        sessionImpl.initialize(cb);
20733eefa59e37291abc72edd1c30b5469a21993dbbbDongwon Kang                    }
20746a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    args.recycle();
20753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    return;
20763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2077a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                case DO_CREATE_RECORDING_SESSION: {
2078a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
2079a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg1;
2080a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    String inputId = (String) args.arg2;
2081a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    args.recycle();
2082a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    RecordingSession recordingSessionImpl = onCreateRecordingSession(inputId);
2083a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    if (recordingSessionImpl == null) {
2084a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        try {
2085a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                            // Failed to create a recording session.
2086a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                            cb.onSessionCreated(null, null);
2087a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        } catch (RemoteException e) {
2088a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                            Log.e(TAG, "error in onSessionCreated", e);
2089a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        }
2090a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        return;
2091a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
2092a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
2093a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                            recordingSessionImpl);
2094a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    try {
2095a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        cb.onSessionCreated(stub, null);
2096a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } catch (RemoteException e) {
2097a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        Log.e(TAG, "error in onSessionCreated", e);
2098a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
2099a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    recordingSessionImpl.initialize(cb);
2100a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    return;
2101a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
21021abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                case DO_ADD_HARDWARE_INPUT: {
2103187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
2104187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    TvInputInfo inputInfo = onHardwareAdded(hardwareInfo);
2105187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    if (inputInfo != null) {
21061abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                        broadcastAddHardwareInput(hardwareInfo.getDeviceId(), inputInfo);
21074f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    }
21084f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    return;
21094f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                }
21101abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                case DO_REMOVE_HARDWARE_INPUT: {
21114f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
21124f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    String inputId = onHardwareRemoved(hardwareInfo);
21134f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    if (inputId != null) {
21141abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                        broadcastRemoveHardwareInput(inputId);
21154f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    }
21164f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    return;
21174f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                }
21181abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                case DO_ADD_HDMI_INPUT: {
2119546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
2120546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    TvInputInfo inputInfo = onHdmiDeviceAdded(deviceInfo);
21214f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    if (inputInfo != null) {
21221abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                        broadcastAddHdmiInput(deviceInfo.getId(), inputInfo);
2123187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2124187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    return;
2125187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
21261abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                case DO_REMOVE_HDMI_INPUT: {
2127546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
2128546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    String inputId = onHdmiDeviceRemoved(deviceInfo);
2129187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    if (inputId != null) {
21301abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                        broadcastRemoveHardwareInput(inputId);
2131187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2132187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    return;
2133187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
21343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                default: {
21353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Log.w(TAG, "Unhandled message code: " + msg.what);
21363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    return;
21373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
21383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
21393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
21403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
21413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
2142