TvInputManagerService.java revision 8c375feb686dee8b6e8a9c69100de88c4d61afdc
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
173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seopackage com.android.server.tv;
183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
19969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
20e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
21969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.app.ActivityManager;
233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.BroadcastReceiver;
243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.ComponentName;
255c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport android.content.ContentProviderOperation;
265c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport android.content.ContentProviderResult;
2731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentResolver;
2831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentUris;
2931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentValues;
303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Context;
313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Intent;
323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.IntentFilter;
335c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport android.content.OperationApplicationException;
343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.ServiceConnection;
359c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seoimport android.content.pm.ActivityInfo;
363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.PackageManager;
378c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seoimport android.content.pm.PackageManager.NameNotFoundException;
383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.ResolveInfo;
393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.ServiceInfo;
409a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.graphics.Rect;
41e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kimimport android.hardware.hdmi.HdmiControlManager;
4261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
434b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seoimport android.media.PlaybackParams;
4458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport android.media.tv.DvbDeviceInfo;
45d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputClient;
46d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
47d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
48d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputManager;
49969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.ITvInputManagerCallback;
50d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputService;
51d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputServiceCallback;
52d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputSession;
53d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputSessionCallback;
54783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.media.tv.TvContentRating;
559c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seoimport android.media.tv.TvContentRatingSystemInfo;
56d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvContract;
57d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputHardwareInfo;
58d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputInfo;
599c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seoimport android.media.tv.TvInputManager;
60d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputService;
61c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heoimport android.media.tv.TvStreamConfig;
621f213914c45c23c653f721690da2ce0718e63139Dongwon Kangimport android.media.tv.TvTrackInfo;
633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.net.Uri;
643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Binder;
65832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Choimport android.os.Bundle;
6631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Handler;
673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.IBinder;
6831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Looper;
6931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Message;
7058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport android.os.ParcelFileDescriptor;
713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Process;
723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.RemoteException;
733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.UserHandle;
749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.util.Slog;
753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.util.SparseArray;
766a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputChannel;
773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.view.Surface;
783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.internal.content.PackageMonitor;
8031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.internal.os.SomeArgs;
81e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport com.android.internal.util.IndentingPrintWriter;
8231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.server.IoThread;
833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.server.SystemService;
843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
85e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Leeimport org.xmlpull.v1.XmlPullParserException;
86e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee
8758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.io.File;
88e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport java.io.FileDescriptor;
8958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.io.FileNotFoundException;
90e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Leeimport java.io.IOException;
91e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport java.io.PrintWriter;
923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.ArrayList;
9319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Leeimport java.util.Arrays;
9458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.util.Collections;
953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.HashMap;
965c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport java.util.HashSet;
97187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport java.util.Iterator;
983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.List;
993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.Map;
1005c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport java.util.Set;
10158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.util.regex.Matcher;
10258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.util.regex.Pattern;
1033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo/** This class provides a system service that manages television inputs. */
1053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seopublic final class TvInputManagerService extends SystemService {
106ee2ec05ed7c0d3cb9115f4ddd7c3613269c4a57bJae Seo    private static final boolean DEBUG = false;
1073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final String TAG = "TvInputManagerService";
1083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung    // Pattern for selecting the DVB frontend devices from the list of files in the /dev directory.
11058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung    private static final Pattern sFrontEndDevicePattern =
11158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$");
11258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
1133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Context mContext;
114c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHardwareManager mTvInputHardwareManager;
1153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A global lock.
1173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Object mLock = new Object();
1183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // ID of the current user.
1203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int mCurrentUserId = UserHandle.USER_OWNER;
1213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A map from user id to UserState.
1236e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo    private final SparseArray<UserState> mUserStates = new SparseArray<>();
1243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1257eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo    private final WatchLogHandler mWatchLogHandler;
12631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public TvInputManagerService(Context context) {
1283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        super(context);
12931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext = context;
1318c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        mWatchLogHandler = new WatchLogHandler(mContext.getContentResolver(),
1328c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                IoThread.get().getLooper());
133187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
13431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
136783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mUserStates.put(mCurrentUserId, new UserState(mContext, mCurrentUserId));
1373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    @Override
1413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public void onStart() {
1423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
1433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1450ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    @Override
1460ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    public void onBootPhase(int phase) {
1470ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1480ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            registerBroadcastReceivers();
149187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1500ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            synchronized (mLock) {
15119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                buildTvInputListLocked(mCurrentUserId, null);
1529c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                buildTvContentRatingSystemListLocked(mCurrentUserId);
1530ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            }
1540ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee        }
155969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        mTvInputHardwareManager.onBootPhase(phase);
1560ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    }
1570ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee
1583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void registerBroadcastReceivers() {
1593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageMonitor monitor = new PackageMonitor() {
16019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            private void buildTvInputList(String[] packages) {
16119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                synchronized (mLock) {
16219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    buildTvInputListLocked(getChangingUserId(), packages);
16319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    buildTvContentRatingSystemListLocked(getChangingUserId());
16419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
16519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
16619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
16719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            @Override
16819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            public void onPackageUpdateFinished(String packageName, int uid) {
16919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
17019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // This callback is invoked when the TV input is reinstalled.
17119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // In this case, isReplacing() always returns true.
17219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                buildTvInputList(new String[] { packageName });
17319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
17419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
17519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            @Override
17619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            public void onPackagesAvailable(String[] packages) {
17719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (DEBUG) {
17819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
17919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
18019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // This callback is invoked when the media on which some packages exist become
18119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // available.
18219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (isReplacing()) {
18319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    buildTvInputList(packages);
18419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
18519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
18619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
18719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            @Override
18819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            public void onPackagesUnavailable(String[] packages) {
18919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // This callback is invoked when the media on which some packages exist become
19019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // unavailable.
19119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (DEBUG)  {
19219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
19319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                            + ")");
19419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
19519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (isReplacing()) {
19619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    buildTvInputList(packages);
19719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
19819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
19919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
2003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
2013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSomePackagesChanged() {
20219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
20319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // the TV inputs.
204426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
20519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (isReplacing()) {
20619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
20719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    // When the package is updated, buildTvInputListLocked is called in other
20819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    // methods instead.
20919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    return;
2103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
21119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                buildTvInputList(null);
2123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2135c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
2145c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            @Override
21531a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang            public boolean onPackageChanged(String packageName, int uid, String[] components) {
21631a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang                // The input list needs to be updated in any cases, regardless of whether
21731a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang                // it happened to the whole package or a specific component. Returning true so that
21831a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang                // the update can be handled in {@link #onSomePackagesChanged}.
21931a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang                return true;
22031a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang            }
22131a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang
22231a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang            @Override
2235c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            public void onPackageRemoved(String packageName, int uid) {
2245c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                synchronized (mLock) {
22519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    UserState userState = getUserStateLocked(getChangingUserId());
226969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    if (!userState.packageSet.contains(packageName)) {
2275c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        // Not a TV input package.
2285c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        return;
2295c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    }
2305c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                }
2315c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
2326e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                ArrayList<ContentProviderOperation> operations = new ArrayList<>();
2335c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
2345c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?";
2355c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                String[] selectionArgs = { packageName };
2365c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
2375c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI)
2385c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        .withSelection(selection, selectionArgs).build());
2395c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI)
2405c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        .withSelection(selection, selectionArgs).build());
2415c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                operations.add(ContentProviderOperation
2425c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        .newDelete(TvContract.WatchedPrograms.CONTENT_URI)
2435c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        .withSelection(selection, selectionArgs).build());
2445c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
2455c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                ContentProviderResult[] results = null;
2465c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                try {
2478c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    ContentResolver cr = getContentResolverForUser(getChangingUserId());
2488c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    results = cr.applyBatch(TvContract.AUTHORITY, operations);
2495c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                } catch (RemoteException | OperationApplicationException e) {
250fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    Slog.e(TAG, "error in applyBatch", e);
2515c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                }
2525c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
2535c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                if (DEBUG) {
2545c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid
2555c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                            + ")");
2565c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    Slog.d(TAG, "results=" + results);
2575c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                }
2585c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            }
2593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
2603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        monitor.register(mContext, null, UserHandle.ALL, true);
2613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        IntentFilter intentFilter = new IntentFilter();
2633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
2643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
2653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext.registerReceiverAsUser(new BroadcastReceiver() {
2663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
2673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onReceive(Context context, Intent intent) {
2683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                String action = intent.getAction();
2693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
2703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
2713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
2723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
2733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }, UserHandle.ALL, intentFilter, null, null);
2763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2789e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee    private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) {
279187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE,
2809e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
281187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
282187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
28319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee    private void buildTvInputListLocked(int userId, String[] updatedPackages) {
2843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
285969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        userState.packageSet.clear();
2863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
28719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        if (DEBUG) Slog.d(TAG, "buildTvInputList");
2883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageManager pm = mContext.getPackageManager();
28976976fae6fbe2f30ba209575557da153e29be33bJae Seo        List<ResolveInfo> services = pm.queryIntentServicesAsUser(
290e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                new Intent(TvInputService.SERVICE_INTERFACE),
29176976fae6fbe2f30ba209575557da153e29be33bJae Seo                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
29276976fae6fbe2f30ba209575557da153e29be33bJae Seo                userId);
2936e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        List<TvInputInfo> inputList = new ArrayList<>();
2943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        for (ResolveInfo ri : services) {
2953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            ServiceInfo si = ri.serviceInfo;
2963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
2979a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
2983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + android.Manifest.permission.BIND_TV_INPUT);
2993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                continue;
3003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
3019cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo
3029cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            ComponentName component = new ComponentName(si.packageName, si.name);
3039cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            if (hasHardwarePermission(pm, component)) {
3049cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                ServiceState serviceState = userState.serviceStateMap.get(component);
3059cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                if (serviceState == null) {
3069cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    // We see this hardware TV input service for the first time; we need to
3079cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    // prepare the ServiceState object so that we can connect to the service and
3089cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    // let it add TvInputInfo objects to mInputList if there's any.
3099cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    serviceState = new ServiceState(component, userId);
3109cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    userState.serviceStateMap.put(component, serviceState);
311f271eacba7997d2751c336153634fac53bc4d660Wonsik Kim                    updateServiceConnectionLocked(component, userId);
312187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                } else {
313fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    inputList.addAll(serviceState.inputList);
314187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
3159cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            } else {
3169cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                try {
3179cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    inputList.add(TvInputInfo.createTvInputInfo(mContext, ri));
3189cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                } catch (XmlPullParserException | IOException e) {
319fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    Slog.e(TAG, "failed to load TV input " + si.name, e);
3209cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    continue;
321969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
3229cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            }
3239cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            userState.packageSet.add(si.packageName);
3249cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo        }
325187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
3266e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        Map<String, TvInputState> inputMap = new HashMap<>();
3279cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo        for (TvInputInfo info : inputList) {
328fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            if (DEBUG) {
329fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.d(TAG, "add " + info.getId());
330fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            }
3319cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            TvInputState state = userState.inputMap.get(info.getId());
3329cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            if (state == null) {
3339cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                state = new TvInputState();
334e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            }
335fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            state.info = info;
3369cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            inputMap.put(info.getId(), state);
3373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3388e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
3398e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        for (String inputId : inputMap.keySet()) {
3408e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            if (!userState.inputMap.containsKey(inputId)) {
3418e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                notifyInputAddedLocked(userState, inputId);
34219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            } else if (updatedPackages != null) {
34319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // Notify the package updates
3449c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                ComponentName component = inputMap.get(inputId).info.getComponent();
34519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                for (String updatedPackage : updatedPackages) {
3469c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    if (component.getPackageName().equals(updatedPackage)) {
3479c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                        updateServiceConnectionLocked(component, userId);
34819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                        notifyInputUpdatedLocked(userState, inputId);
34919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                        break;
35019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    }
35119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
3528e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            }
3538e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
3548e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
3558e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        for (String inputId : userState.inputMap.keySet()) {
3568e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            if (!inputMap.containsKey(inputId)) {
357fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                TvInputInfo info = userState.inputMap.get(inputId).info;
358426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
359426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                if (serviceState != null) {
360426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
361426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                }
3628e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                notifyInputRemovedLocked(userState, inputId);
3638e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            }
3648e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
3658e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
3668e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        userState.inputMap.clear();
3678e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        userState.inputMap = inputMap;
3689c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo    }
3699c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo
3709c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo    private void buildTvContentRatingSystemListLocked(int userId) {
3719c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        UserState userState = getUserStateLocked(userId);
3729c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        userState.contentRatingSystemList.clear();
3739c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo
3749c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        final PackageManager pm = mContext.getPackageManager();
3759c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS);
3769c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        for (ResolveInfo resolveInfo :
3779c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) {
3789c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            ActivityInfo receiver = resolveInfo.activityInfo;
3799c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            Bundle metaData = receiver.metaData;
3809c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            if (metaData == null) {
3819c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                continue;
3829c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            }
3835c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim
3849c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS);
3859c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            if (xmlResId == 0) {
3869c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                Slog.w(TAG, "Missing meta-data '"
3879c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                        + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver "
3889c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                        + receiver.packageName + "/" + receiver.name);
3899c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                continue;
3905c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            }
3919c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            userState.contentRatingSystemList.add(
3929c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                    TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId,
3939c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                            receiver.applicationInfo));
3945c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim        }
3953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void switchUser(int userId) {
3983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
3993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (mCurrentUserId == userId) {
4003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
4013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4028c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            clearSessionAndServiceStatesLocked(mUserStates.get(mCurrentUserId));
4033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4048c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            mCurrentUserId = userId;
4053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            UserState userState = mUserStates.get(userId);
4063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (userState == null) {
407783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                userState = new UserState(mContext, userId);
4088c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                mUserStates.put(userId, userState);
4093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
41019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            buildTvInputListLocked(userId, null);
4119c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            buildTvContentRatingSystemListLocked(userId);
4128c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_SWITCH_CONTENT_RESOLVER,
4138c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    getContentResolverForUser(userId)).sendToTarget();
4143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void removeUser(int userId) {
4183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
419b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            UserState userState = mUserStates.get(userId);
420b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            if (userState == null) {
421b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo                return;
422b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            }
4238c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            clearSessionAndServiceStatesLocked(userState);
4243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
425fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            // Clear everything else.
426fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            userState.inputMap.clear();
427fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            userState.packageSet.clear();
4289c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            userState.contentRatingSystemList.clear();
42972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            userState.clientStateMap.clear();
430fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            userState.callbackSet.clear();
431fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            userState.mainSessionToken = null;
43272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
4333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.remove(userId);
4343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4378c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo    private void clearSessionAndServiceStatesLocked(UserState userState) {
4388c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        // Release created sessions.
4398c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        for (SessionState state : userState.sessionStateMap.values()) {
4408c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            if (state.session != null) {
4418c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                try {
4428c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    state.session.release();
4438c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                } catch (RemoteException e) {
4448c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    Slog.e(TAG, "error in release", e);
4458c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                }
4468c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            }
4478c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        }
4488c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        userState.sessionStateMap.clear();
4498c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo
4508c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        // Unregister all callbacks and unbind all services.
4518c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        for (ServiceState serviceState : userState.serviceStateMap.values()) {
4528c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            if (serviceState.callback != null) {
4538c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                try {
4548c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    serviceState.service.unregisterCallback(serviceState.callback);
4558c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                } catch (RemoteException e) {
4568c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    Slog.e(TAG, "error in unregisterCallback", e);
4578c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                }
4588c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            }
4598c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            mContext.unbindService(serviceState.connection);
4608c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        }
4618c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        userState.serviceStateMap.clear();
4628c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo    }
4638c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo
4648c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo    private ContentResolver getContentResolverForUser(int userId) {
4658c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        UserHandle user = new UserHandle(userId);
4668c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        Context context;
4678c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        try {
4688c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            context = mContext.createPackageContextAsUser("android", 0, user);
4698c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        } catch (NameNotFoundException e) {
4708c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            Slog.e(TAG, "failed to create package contenxt as user " + user);
4718c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            context = mContext;
4728c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        }
4738c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        return context.getContentResolver();
4748c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo    }
4758c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo
4763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private UserState getUserStateLocked(int userId) {
4773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = mUserStates.get(userId);
4783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (userState == null) {
4793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("User state not found for user ID " + userId);
4803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return userState;
4823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4849e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee    private ServiceState getServiceStateLocked(ComponentName component, int userId) {
4853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
4869e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        ServiceState serviceState = userState.serviceStateMap.get(component);
4873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
4889e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            throw new IllegalStateException("Service state not found for " + component + " (userId="
4897de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    + userId + ")");
4903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return serviceState;
4923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
4953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
4963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
4973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (sessionState == null) {
498fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            throw new SessionNotFoundException("Session state not found for token " + sessionToken);
4993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Only the application that requested this session or the system can access it.
501fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
5023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new SecurityException("Illegal access to the session with token " + sessionToken
5033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + " from uid " + callingUid);
5043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5052b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        return sessionState;
5062b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
5072b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5082b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
5094c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
5104c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee    }
5114c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee
5124c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee    private ITvInputSession getSessionLocked(SessionState sessionState) {
513fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        ITvInputSession session = sessionState.session;
5143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (session == null) {
5154c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            throw new IllegalStateException("Session not yet created for token "
516fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    + sessionState.sessionToken);
5173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return session;
5193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
5223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            String methodName) {
5233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
5243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                false, methodName, null);
5253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
527187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private static boolean shouldMaintainConnection(ServiceState serviceState) {
528fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        return !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
529fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        // TODO: Find a way to maintain connection to hardware TV input service only when necessary.
530187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
531187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
5329e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee    private void updateServiceConnectionLocked(ComponentName component, int userId) {
5333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
5349e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        ServiceState serviceState = userState.serviceStateMap.get(component);
5353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
5363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return;
5373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
538fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        if (serviceState.reconnecting) {
539fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            if (!serviceState.sessionTokens.isEmpty()) {
5402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                // wait until all the sessions are removed.
5412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                return;
5422b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
543fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            serviceState.reconnecting = false;
5442b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
545187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        boolean maintainConnection = shouldMaintainConnection(serviceState);
546fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        if (serviceState.service == null && maintainConnection && userId == mCurrentUserId) {
5473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is not yet connected but its state indicates that we
5483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // have pending requests. Then, connect the service.
549fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            if (serviceState.bound) {
5503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // We have already bound to the service so we don't try to bind again until after we
5513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // unbind later on.
5523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
5533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
5559e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
5563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
557d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim
5589e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
559fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            serviceState.bound = mContext.bindServiceAsUser(
560d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    i, serviceState.connection,
561d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
562d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    new UserHandle(userId));
563fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        } else if (serviceState.service != null && !maintainConnection) {
5643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is already connected but its state indicates that we have
5653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // nothing to do with it. Then, disconnect the service.
5663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
5679e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                Slog.d(TAG, "unbindService(service=" + component + ")");
5683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
569fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            mContext.unbindService(serviceState.connection);
5709e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            userState.serviceStateMap.remove(component);
5713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
574426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang    private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
575426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang            String inputId, int userId) {
576426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang        // Let clients know the create session requests are failed.
577426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang        UserState userState = getUserStateLocked(userId);
578f7f49ddade34744d5386f9bf52ab9ba4f981fce7Dongwon Kang        List<SessionState> sessionsToAbort = new ArrayList<>();
579fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        for (IBinder sessionToken : serviceState.sessionTokens) {
580426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang            SessionState sessionState = userState.sessionStateMap.get(sessionToken);
581fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            if (sessionState.session == null && (inputId == null
582fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    || sessionState.info.getId().equals(inputId))) {
583f7f49ddade34744d5386f9bf52ab9ba4f981fce7Dongwon Kang                sessionsToAbort.add(sessionState);
584426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang            }
585426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang        }
586f7f49ddade34744d5386f9bf52ab9ba4f981fce7Dongwon Kang        for (SessionState sessionState : sessionsToAbort) {
587fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
588fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            sendSessionTokenToClientLocked(sessionState.client,
589fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    sessionState.info.getId(), null, null, sessionState.seq);
590f7f49ddade34744d5386f9bf52ab9ba4f981fce7Dongwon Kang        }
591fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        updateServiceConnectionLocked(serviceState.component, userId);
592426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang    }
593426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang
594fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
595fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            int userId) {
596fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        UserState userState = getUserStateLocked(userId);
597fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
5983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (DEBUG) {
599fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")");
6003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
601fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
6026a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
6033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Set up a callback to send the session token.
604fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
6053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Create a session. When failed, send a null token immediately.
6073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
608fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            service.createSession(channels[1], callback, sessionState.info.getId());
6093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException e) {
6109a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in createSession", e);
611fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang            removeSessionStateLocked(sessionToken, userId);
612fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            sendSessionTokenToClientLocked(sessionState.client, sessionState.info.getId(), null,
613fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    null, sessionState.seq);
6143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6156a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        channels[1].dispose();
6163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
6173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
618d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
6195c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            IBinder sessionToken, InputChannel channel, int seq) {
6203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
621d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            client.onSessionCreated(inputId, sessionToken, channel, seq);
622fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo        } catch (RemoteException e) {
623fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            Slog.e(TAG, "error in onSessionCreated", e);
6243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6252b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
6263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6272b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
628fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        SessionState sessionState = null;
629fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        try {
630fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
631fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            if (sessionState.session != null) {
632fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                UserState userState = getUserStateLocked(userId);
633fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                if (sessionToken == userState.mainSessionToken) {
634fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    setMainLocked(sessionToken, false, callingUid, userId);
635fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                }
636fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                sessionState.session.release();
6372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
638fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        } catch (RemoteException | SessionNotFoundException e) {
639fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            Slog.e(TAG, "error in releaseSession", e);
640fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        } finally {
641fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            if (sessionState != null) {
642fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                sessionState.session = null;
643fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            }
6443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        removeSessionStateLocked(sessionToken, userId);
6463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
6473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
648fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
649fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        UserState userState = getUserStateLocked(userId);
650abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee        if (sessionToken == userState.mainSessionToken) {
65115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            if (DEBUG) {
65215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                Slog.d(TAG, "mainSessionToken=null");
65315c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            }
654abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee            userState.mainSessionToken = null;
655abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee        }
656abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee
657abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee        // Remove the session state from the global session state map of the current user.
658fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
659fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
6608d4ded0058de5c573ccf79c4596bf5eb1b14fad3Chulwoo Lee        if (sessionState == null) {
6618d4ded0058de5c573ccf79c4596bf5eb1b14fad3Chulwoo Lee            return;
6628d4ded0058de5c573ccf79c4596bf5eb1b14fad3Chulwoo Lee        }
6638d4ded0058de5c573ccf79c4596bf5eb1b14fad3Chulwoo Lee
66472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // Also remove the session token from the session token list of the current client and
66572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // service.
666fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
66772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (clientState != null) {
668fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            clientState.sessionTokens.remove(sessionToken);
66972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            if (clientState.isEmpty()) {
670fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                userState.clientStateMap.remove(sessionState.client.asBinder());
67172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
67272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
67372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
674fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        TvInputInfo info = sessionState.info;
675187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        if (info != null) {
676187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
677187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            if (serviceState != null) {
678fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                serviceState.sessionTokens.remove(sessionToken);
679187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
680fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        }
681fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        updateServiceConnectionLocked(sessionState.info.getComponent(), userId);
6827eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
6837eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // Log the end of watch.
6847eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        SomeArgs args = SomeArgs.obtain();
6857eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        args.arg1 = sessionToken;
6867eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        args.arg2 = System.currentTimeMillis();
6877eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget();
688fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    }
689fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
69015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee    private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
69115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee        try {
692fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
693fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            if (sessionState.hardwareSessionToken != null) {
694fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
695fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                        Process.SYSTEM_UID, userId);
696fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            }
697fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId);
698fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            if (!serviceState.isHardware) {
699fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                return;
700fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            }
701fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            ITvInputSession session = getSessionLocked(sessionState);
70215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            session.setMain(isMain);
703fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        } catch (RemoteException | SessionNotFoundException e) {
70415c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            Slog.e(TAG, "error in setMain", e);
70515c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee        }
70615c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee    }
70715c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee
7088e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    private void notifyInputAddedLocked(UserState userState, String inputId) {
7098e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        if (DEBUG) {
710fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")");
7118e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
7128e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        for (ITvInputManagerCallback callback : userState.callbackSet) {
7138e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            try {
7148e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                callback.onInputAdded(inputId);
7158e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            } catch (RemoteException e) {
716fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.e(TAG, "failed to report added input to callback", e);
7178e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            }
7188e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
7198e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    }
7208e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
7218e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    private void notifyInputRemovedLocked(UserState userState, String inputId) {
7228e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        if (DEBUG) {
723fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")");
7248e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
7258e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        for (ITvInputManagerCallback callback : userState.callbackSet) {
7268e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            try {
7278e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                callback.onInputRemoved(inputId);
7288e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            } catch (RemoteException e) {
729fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.e(TAG, "failed to report removed input to callback", e);
7308e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            }
7318e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
7328e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    }
7338e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
73419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee    private void notifyInputUpdatedLocked(UserState userState, String inputId) {
73519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        if (DEBUG) {
73619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")");
73719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        }
73819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        for (ITvInputManagerCallback callback : userState.callbackSet) {
73919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            try {
74019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                callback.onInputUpdated(inputId);
74119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            } catch (RemoteException e) {
74219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                Slog.e(TAG, "failed to report updated input to callback", e);
74319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
74419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        }
74519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee    }
74619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
7478e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    private void notifyInputStateChangedLocked(UserState userState, String inputId,
748969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            int state, ITvInputManagerCallback targetCallback) {
749969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (DEBUG) {
750fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId
751fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    + ", state=" + state + ")");
752969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
753969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (targetCallback == null) {
754969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (ITvInputManagerCallback callback : userState.callbackSet) {
755969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                try {
756969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    callback.onInputStateChanged(inputId, state);
757969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                } catch (RemoteException e) {
758fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    Slog.e(TAG, "failed to report state change to callback", e);
759969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
760969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
761969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        } else {
7622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
763969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                targetCallback.onInputStateChanged(inputId, state);
7642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
765fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.e(TAG, "failed to report state change to callback", e);
7662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
7672b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
7682b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
7692b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
770969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private void setStateLocked(String inputId, int state, int userId) {
771969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        UserState userState = getUserStateLocked(userId);
772969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        TvInputState inputState = userState.inputMap.get(inputId);
773fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
774fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        int oldState = inputState.state;
775fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        inputState.state = state;
776fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        if (serviceState != null && serviceState.service == null
777187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                && shouldMaintainConnection(serviceState)) {
778969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            // We don't notify state change while reconnecting. It should remain disconnected.
779969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return;
780969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
781969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (oldState != state) {
7828e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            notifyInputStateChangedLocked(userState, inputId, state, null);
783969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
784969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
785969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
7863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class BinderService extends ITvInputManager.Stub {
7873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public List<TvInputInfo> getTvInputList(int userId) {
7893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
7903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getTvInputList");
7913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
7956e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    List<TvInputInfo> inputList = new ArrayList<>();
796969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (TvInputState state : userState.inputMap.values()) {
797fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        inputList.add(state.info);
7983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
799969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    return inputList;
8003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
8023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
8033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
804b375805f3b1672e68d1511565af4700e5fa8491dJae Seo        }
805b375805f3b1672e68d1511565af4700e5fa8491dJae Seo
806b375805f3b1672e68d1511565af4700e5fa8491dJae Seo        @Override
807b375805f3b1672e68d1511565af4700e5fa8491dJae Seo        public TvInputInfo getTvInputInfo(String inputId, int userId) {
808b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
809b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                    Binder.getCallingUid(), userId, "getTvInputInfo");
810b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            final long identity = Binder.clearCallingIdentity();
811b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            try {
812b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                synchronized (mLock) {
813b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
814b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                    TvInputState state = userState.inputMap.get(inputId);
815fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return state == null ? null : state.info;
816b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                }
817b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            } finally {
818b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                Binder.restoreCallingIdentity(identity);
819b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            }
8203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
823993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang        public int getTvInputState(String inputId, int userId) {
824993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
825993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                    Binder.getCallingUid(), userId, "getTvInputState");
826993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            final long identity = Binder.clearCallingIdentity();
827993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            try {
828993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                synchronized (mLock) {
829993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                    UserState userState = getUserStateLocked(resolvedUserId);
830993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                    TvInputState state = userState.inputMap.get(inputId);
83182fce64530d19a4da1c02d424fb2515feafe6a70Jae Seo                    return state == null ? INPUT_STATE_CONNECTED : state.state;
832993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                }
833993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            } finally {
834993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                Binder.restoreCallingIdentity(identity);
835993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            }
836993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang        }
837993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang
838993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang        @Override
8399c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) {
8405c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
8419c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                    Binder.getCallingUid(), userId, "getTvContentRatingSystemList");
8425c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            final long identity = Binder.clearCallingIdentity();
8435c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            try {
8445c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim                synchronized (mLock) {
8455c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim                    UserState userState = getUserStateLocked(resolvedUserId);
8469c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                    return userState.contentRatingSystemList;
8475c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim                }
8485c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            } finally {
8495c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim                Binder.restoreCallingIdentity(identity);
8505c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            }
8515c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim        }
8525c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim
8535c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim        @Override
854969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void registerCallback(final ITvInputManagerCallback callback, int userId) {
8553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
8563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "registerCallback");
8573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
8583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
8593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
860fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    final UserState userState = getUserStateLocked(resolvedUserId);
861969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    userState.callbackSet.add(callback);
862fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    try {
863fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        callback.asBinder().linkToDeath(new IBinder.DeathRecipient() {
864fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                            @Override
865fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                            public void binderDied() {
866fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                synchronized (mLock) {
867fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                    if (userState.callbackSet != null) {
868fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                        userState.callbackSet.remove(callback);
869fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                    }
870fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                }
871fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                            }
872fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        }, 0);
873fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    } catch (RemoteException e) {
874fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        Slog.e(TAG, "client process has already died", e);
875fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    }
8763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
8783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
8793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
883969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
8843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
8853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "unregisterCallback");
8863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
8873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
8883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
889969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    UserState userState = getUserStateLocked(resolvedUserId);
890969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    userState.callbackSet.remove(callback);
8913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
8933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
8943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
898783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public boolean isParentalControlsEnabled(int userId) {
899783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
900783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "isParentalControlsEnabled");
901783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
902783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
903783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
904783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
905783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    return userState.persistentDataStore.isParentalControlsEnabled();
906783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
907783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
908783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
909783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
910783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
911783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
912783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
913783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public void setParentalControlsEnabled(boolean enabled, int userId) {
914783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            ensureParentalControlsPermission();
915783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
916783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "setParentalControlsEnabled");
917783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
918783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
919783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
920783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
921783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    userState.persistentDataStore.setParentalControlsEnabled(enabled);
922783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
923783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
924783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
925783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
926783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
927783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
928783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
929783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public boolean isRatingBlocked(String rating, int userId) {
930783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
931783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "isRatingBlocked");
932783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
933783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
934783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
935783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
936783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    return userState.persistentDataStore.isRatingBlocked(
937783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            TvContentRating.unflattenFromString(rating));
938783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
939783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
940783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
941783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
942783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
943783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
944783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
945783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public List<String> getBlockedRatings(int userId) {
946783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
947783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "getBlockedRatings");
948783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
949783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
950783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
951783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
9526e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    List<String> ratings = new ArrayList<>();
953783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    for (TvContentRating rating
954783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            : userState.persistentDataStore.getBlockedRatings()) {
955783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                        ratings.add(rating.flattenToString());
956783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    }
957783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    return ratings;
958783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
959783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
960783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
961783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
962783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
963783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
964783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
965783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public void addBlockedRating(String rating, int userId) {
966783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            ensureParentalControlsPermission();
967783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
968783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "addBlockedRating");
969783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
970783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
971783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
972783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
973783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    userState.persistentDataStore.addBlockedRating(
974783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            TvContentRating.unflattenFromString(rating));
975783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
976783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
977783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
978783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
979783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
980783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
981783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
982783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public void removeBlockedRating(String rating, int userId) {
983783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            ensureParentalControlsPermission();
984783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
985783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "removeBlockedRating");
986783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
987783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
988783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
989783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
990783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    userState.persistentDataStore.removeBlockedRating(
991783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            TvContentRating.unflattenFromString(rating));
992783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
993783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
994783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
995783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
996783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
997783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
998783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        private void ensureParentalControlsPermission() {
999fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo            if (mContext.checkCallingPermission(
1000fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo                    android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
1001fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo                    != PackageManager.PERMISSION_GRANTED) {
1002fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo                throw new SecurityException(
1003fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo                        "The caller does not have parental controls permission");
1004fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo            }
1005783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
1006783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1007783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
1008d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void createSession(final ITvInputClient client, final String inputId,
10093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int seq, int userId) {
10103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
10113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
10123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "createSession");
10133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
10143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
10153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
10163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
1017426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    TvInputState inputState = userState.inputMap.get(inputId);
1018426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    if (inputState == null) {
1019426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                        Slog.w(TAG, "Failed to find input state for inputId=" + inputId);
1020426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1021426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                        return;
1022426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    }
1023fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    TvInputInfo info = inputState.info;
1024187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
10253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
1026187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        serviceState = new ServiceState(info.getComponent(), resolvedUserId);
1027187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        userState.serviceStateMap.put(info.getComponent(), serviceState);
10283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
10292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send a null token immediately while reconnecting.
10306e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    if (serviceState.reconnecting) {
10315c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
10322b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        return;
10332b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
10342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
10352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Create a new session token and a session state.
10362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    IBinder sessionToken = new Binder();
1037187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    SessionState sessionState = new SessionState(sessionToken, info, client,
103872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                            seq, callingUid, resolvedUserId);
10392b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
10402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Add them to the global session state map of the current user.
10412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    userState.sessionStateMap.put(sessionToken, sessionState);
10422b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
10432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Also, add them to the session state map of the current service.
1044fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.sessionTokens.add(sessionToken);
10453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1046fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (serviceState.service != null) {
1047fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        createSessionInternalLocked(serviceState.service, sessionToken,
10487de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                                resolvedUserId);
10493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
1050187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
10513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
10523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
10533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
10543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
10553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
10593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void releaseSession(IBinder sessionToken, int userId) {
106015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            if (DEBUG) {
1061fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
106215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            }
10633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
10643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
10653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "releaseSession");
10663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
10673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
10683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
10692b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
10703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
10713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
10723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
10733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
10774c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        public void setMainSession(IBinder sessionToken, int userId) {
107815c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            if (DEBUG) {
1079fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")");
108015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            }
10814c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            final int callingUid = Binder.getCallingUid();
10824c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
10834c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    userId, "setMainSession");
10844c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            final long identity = Binder.clearCallingIdentity();
10854c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            try {
10864c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                synchronized (mLock) {
1087982abe693f66037ca265b88057eceb5a3e815182Ji-Hwan Lee                    UserState userState = getUserStateLocked(resolvedUserId);
1088956afc2ba79f50bb8025c6d334653e3c3419b480Ji-Hwan Lee                    if (userState.mainSessionToken == sessionToken) {
10894c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                        return;
10904c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    }
109115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    if (DEBUG) {
109215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                        Slog.d(TAG, "mainSessionToken=" + sessionToken);
1093abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee                    }
109415c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    IBinder oldMainSessionToken = userState.mainSessionToken;
10954c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    userState.mainSessionToken = sessionToken;
10964c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee
1097956afc2ba79f50bb8025c6d334653e3c3419b480Ji-Hwan Lee                    // Inform the new main session first.
109815c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    // See {@link TvInputService.Session#onSetMain}.
109915c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    if (sessionToken != null) {
110015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                        setMainLocked(sessionToken, true, callingUid, userId);
11014c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    }
110215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    if (oldMainSessionToken != null) {
110315c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                        setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId);
11044c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    }
11054c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                }
11064c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            } finally {
11074c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                Binder.restoreCallingIdentity(identity);
11084c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            }
11094c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        }
11104c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee
11114c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        @Override
11123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
11133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
11143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
11153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setSurface");
11163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
11173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
11183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
11193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1120bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1121bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                resolvedUserId);
1122fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionState.hardwareSessionToken == null) {
1123bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            getSessionLocked(sessionState).setSurface(surface);
1124bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        } else {
1125fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            getSessionLocked(sessionState.hardwareSessionToken,
1126bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                    Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
1127bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1128fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
11299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setSurface", e);
11303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
11313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
11323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
1133f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                if (surface != null) {
1134f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    // surface is not used in TvInputManagerService.
1135f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    surface.release();
1136f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                }
11373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
11383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
11393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
11413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
1142e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
1143e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                int height, int userId) {
1144e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final int callingUid = Binder.getCallingUid();
1145e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1146e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    userId, "dispatchSurfaceChanged");
1147e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final long identity = Binder.clearCallingIdentity();
1148e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            try {
1149e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                synchronized (mLock) {
1150e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    try {
1151bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1152bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                resolvedUserId);
1153fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
1154fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                height);
1155fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionState.hardwareSessionToken != null) {
1156fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
1157bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                    resolvedUserId).dispatchSurfaceChanged(format, width, height);
1158bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1159fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
1160e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                        Slog.e(TAG, "error in dispatchSurfaceChanged", e);
1161e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    }
1162e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                }
1163e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            } finally {
1164e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                Binder.restoreCallingIdentity(identity);
1165e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            }
1166e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        }
1167e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho
1168e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        @Override
11693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setVolume(IBinder sessionToken, float volume, int userId) {
1170bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            final float REMOTE_VOLUME_ON = 1.0f;
1171bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            final float REMOTE_VOLUME_OFF = 0f;
11723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
11733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
11743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setVolume");
11753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
11763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
11773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
11783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1179bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1180bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                resolvedUserId);
1181bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        getSessionLocked(sessionState).setVolume(volume);
1182fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionState.hardwareSessionToken != null) {
1183bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            // Here, we let the hardware session know only whether volume is on or
1184bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            // off to prevent that the volume is controlled in the both side.
1185fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            getSessionLocked(sessionState.hardwareSessionToken,
1186bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                    Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
1187bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                            ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
1188bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1189fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
11909a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setVolume", e);
11913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
11923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
11933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
11943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
11953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
11963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
11983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
11991a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim        public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
12003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
12013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
12023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "tune");
12033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
12043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
12053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
12063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
12071a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
12081a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim                                channelUri, params);
1209c22d0c0941ab65ca69977d002c4431394a735c7dJae Seo                        if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
1210008f6d4e326f6372e165bdf342178ecd1e834e2fYoungsang Cho                            // Do not log the watch history for passthrough inputs.
1211008f6d4e326f6372e165bdf342178ecd1e834e2fYoungsang Cho                            return;
1212008f6d4e326f6372e165bdf342178ecd1e834e2fYoungsang Cho                        }
121331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
121431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        UserState userState = getUserStateLocked(resolvedUserId);
121531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
121631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
12177eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        // Log the start of watch.
121831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SomeArgs args = SomeArgs.obtain();
1219fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        args.arg1 = sessionState.info.getComponent().getPackageName();
12207eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        args.arg2 = System.currentTimeMillis();
12217eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        args.arg3 = ContentUris.parseId(channelUri);
12227eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        args.arg4 = params;
12237eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        args.arg5 = sessionToken;
12247eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args)
12257eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                                .sendToTarget();
1226fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
12279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in tune", e);
12283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
12293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
12303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
12313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
12323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
12333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
12349a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
12359a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
1236a90338396c90f19b062b696cdb1ffcb8600755b2Jae Seo        public void unblockContent(
12379bf671f8ee72b156f16fcf05a3d1c6e093ecba67Sungsoo Lim                IBinder sessionToken, String unblockedRating, int userId) {
1238903d6b72cd572665309633e925485464d08bb25aJaewan Kim            final int callingUid = Binder.getCallingUid();
1239903d6b72cd572665309633e925485464d08bb25aJaewan Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1240903d6b72cd572665309633e925485464d08bb25aJaewan Kim                    userId, "unblockContent");
1241903d6b72cd572665309633e925485464d08bb25aJaewan Kim            final long identity = Binder.clearCallingIdentity();
1242903d6b72cd572665309633e925485464d08bb25aJaewan Kim            try {
1243903d6b72cd572665309633e925485464d08bb25aJaewan Kim                synchronized (mLock) {
1244903d6b72cd572665309633e925485464d08bb25aJaewan Kim                    try {
1245903d6b72cd572665309633e925485464d08bb25aJaewan Kim                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1246a90338396c90f19b062b696cdb1ffcb8600755b2Jae Seo                                .unblockContent(unblockedRating);
1247fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
1248a90338396c90f19b062b696cdb1ffcb8600755b2Jae Seo                        Slog.e(TAG, "error in unblockContent", e);
1249903d6b72cd572665309633e925485464d08bb25aJaewan Kim                    }
1250903d6b72cd572665309633e925485464d08bb25aJaewan Kim                }
1251903d6b72cd572665309633e925485464d08bb25aJaewan Kim            } finally {
1252903d6b72cd572665309633e925485464d08bb25aJaewan Kim                Binder.restoreCallingIdentity(identity);
1253903d6b72cd572665309633e925485464d08bb25aJaewan Kim            }
1254903d6b72cd572665309633e925485464d08bb25aJaewan Kim        }
1255903d6b72cd572665309633e925485464d08bb25aJaewan Kim
1256903d6b72cd572665309633e925485464d08bb25aJaewan Kim        @Override
12572c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
12582c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final int callingUid = Binder.getCallingUid();
12592c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
12602c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    userId, "setCaptionEnabled");
12612c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final long identity = Binder.clearCallingIdentity();
12622c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            try {
12632c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                synchronized (mLock) {
12642c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    try {
12652c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
12662c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                                .setCaptionEnabled(enabled);
1267fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
12682c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                        Slog.e(TAG, "error in setCaptionEnabled", e);
12692c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    }
12702c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                }
12712c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            } finally {
12722c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                Binder.restoreCallingIdentity(identity);
12732c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            }
12742c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        }
12752c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo
12762c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        @Override
127710d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo        public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
12781f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final int callingUid = Binder.getCallingUid();
12791f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
12801f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    userId, "selectTrack");
12811f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final long identity = Binder.clearCallingIdentity();
12821f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            try {
12831f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                synchronized (mLock) {
12841f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    try {
12851f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
128610d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo                                type, trackId);
1287fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
12881f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        Slog.e(TAG, "error in selectTrack", e);
12891f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    }
12901f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                }
12911f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            } finally {
12921f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                Binder.restoreCallingIdentity(identity);
12931f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            }
12941f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        }
12951f213914c45c23c653f721690da2ce0718e63139Dongwon Kang
12961f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        @Override
1297a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
1298a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                int userId) {
1299a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            final int callingUid = Binder.getCallingUid();
1300a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1301a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                    userId, "sendAppPrivateCommand");
1302a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            final long identity = Binder.clearCallingIdentity();
1303a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            try {
1304a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                synchronized (mLock) {
1305a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                    try {
1306a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1307a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                                .appPrivateCommand(command, data);
1308fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
1309fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        Slog.e(TAG, "error in appPrivateCommand", e);
1310a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                    }
1311a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                }
1312a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            } finally {
1313a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                Binder.restoreCallingIdentity(identity);
1314a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            }
1315a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        }
1316a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo
1317a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        @Override
13189a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
13199a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                int userId) {
13209a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
13219a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
13229a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "createOverlayView");
13239a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
13249a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
13259a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
13269a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
13279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
13289a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .createOverlayView(windowToken, frame);
1329fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
13309a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in createOverlayView", e);
13319a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
13329a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
13339a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
13349a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
13359a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
13369a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
13379a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
13389a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
13399a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
13409a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
13419a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
13429a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "relayoutOverlayView");
13439a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
13449a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
13459a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
13469a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
13479a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
13489a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .relayoutOverlayView(frame);
1349fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
13509a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in relayoutOverlayView", e);
13519a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
13529a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
13539a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
13549a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
13559a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
13569a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
13579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
13589a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
13599a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void removeOverlayView(IBinder sessionToken, int userId) {
13609a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
13619a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
13629a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "removeOverlayView");
13639a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
13649a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
13659a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
13669a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
13679a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
13689a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .removeOverlayView();
1369fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
13709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in removeOverlayView", e);
13719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
13729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
13739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
13749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
13759a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
13769a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
1377c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1378c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
13796f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void timeShiftPause(IBinder sessionToken, int userId) {
13806f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
13816f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
13826f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    userId, "timeShiftPause");
13836f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
13846f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
13856f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
13866f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
13876f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
13886f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                                .timeShiftPause();
13896f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
13906f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        Slog.e(TAG, "error in timeShiftPause", e);
13916f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
13926f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
13936f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
13946f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
13956f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
13966f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
13976f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
13986f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
13996f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void timeShiftResume(IBinder sessionToken, int userId) {
14006f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
14016f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
14026f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    userId, "timeShiftResume");
14036f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
14046f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
14056f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
14066f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
14076f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
14086f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                                .timeShiftResume();
14096f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
14106f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        Slog.e(TAG, "error in timeShiftResume", e);
14116f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
14126f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
14136f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
14146f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
14156f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
14166f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
14176f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
14186f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
14196f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId) {
14206f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
14216f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
14226f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    userId, "timeShiftSeekTo");
14236f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
14246f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
14256f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
14266f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
14276f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
14286f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                                .timeShiftSeekTo(timeMs);
14296f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
14306f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        Slog.e(TAG, "error in timeShiftSeekTo", e);
14316f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
14326f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
14336f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
14346f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
14356f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
14366f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
14376f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
14386f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
14394b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seo        public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params,
14406f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                int userId) {
14416f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
14426f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
14434b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seo                    userId, "timeShiftSetPlaybackParams");
14446f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
14456f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
14466f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
14476f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
14486f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
14494b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seo                                .timeShiftSetPlaybackParams(params);
14506f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
14514b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seo                        Slog.e(TAG, "error in timeShiftSetPlaybackParams", e);
14526f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
14536f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
14546f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
14556f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
14566f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
14576f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
14586f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
14596f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
1460465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable,
14616f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                int userId) {
14626f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
14636f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1464465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                    userId, "timeShiftEnablePositionTracking");
14656f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
14666f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
14676f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
14686f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
14696f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1470465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                                .timeShiftEnablePositionTracking(enable);
14716f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
1472465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Slog.e(TAG, "error in timeShiftEnablePositionTracking", e);
14736f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
14746f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
14756f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
14766f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
14776f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
14786f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
14796f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
14806f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
1481c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
1482969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1483c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1484c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
1485c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1486c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1487c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1488c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1489c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.getHardwareList();
1490c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1491c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1492c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1493c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1494c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1495c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1496c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware acquireTvInputHardware(int deviceId,
1497969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                ITvInputHardwareCallback callback, TvInputInfo info, int userId)
1498969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                throws RemoteException {
1499969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1500c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1501c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
1502c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1503c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1504c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1505c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
1506c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1507c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "acquireTvInputHardware");
1508c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1509c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.acquireHardware(
1510969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        deviceId, callback, info, callingUid, resolvedUserId);
1511c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1512c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1513c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1514c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1515c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1516c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1517c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
1518c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
1519969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1520c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1521c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
1522c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1523c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1524c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1525c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
1526c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1527c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "releaseTvInputHardware");
1528c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1529c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mTvInputHardwareManager.releaseHardware(
1530c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, hardware, callingUid, resolvedUserId);
1531c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1532c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1533c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1534c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1535e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1536e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        @Override
153758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        public List<DvbDeviceInfo> getDvbDeviceList() throws RemoteException {
153858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
153958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    != PackageManager.PERMISSION_GRANTED) {
154058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                throw new SecurityException("Requires DVB_DEVICE permission");
154158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            }
154258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
154358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            final long identity = Binder.clearCallingIdentity();
154458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            try {
154558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                ArrayList<DvbDeviceInfo> deviceInfos = new ArrayList<>();
154658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                File devDirectory = new File("/dev");
154758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                for (String fileName : devDirectory.list()) {
154858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    Matcher matcher = sFrontEndDevicePattern.matcher(fileName);
154958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    if (matcher.find()) {
155058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        int adapterId = Integer.parseInt(matcher.group(1));
155158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        int deviceId = Integer.parseInt(matcher.group(2));
155258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        deviceInfos.add(new DvbDeviceInfo(adapterId, deviceId));
155358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    }
155458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                }
155558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                return Collections.unmodifiableList(deviceInfos);
155658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            } finally {
155758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                Binder.restoreCallingIdentity(identity);
155858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            }
155958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        }
156058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
156158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        @Override
156258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device)
156358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                throws RemoteException {
156458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
156558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    != PackageManager.PERMISSION_GRANTED) {
156658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                throw new SecurityException("Requires DVB_DEVICE permission");
156758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            }
156858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
156958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            final long identity = Binder.clearCallingIdentity();
157058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            try {
157158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                String deviceFileName;
157258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                switch (device) {
157358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    case TvInputManager.DVB_DEVICE_DEMUX:
157458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        deviceFileName = String.format("/dev/dvb%d.demux%d", info.getAdapterId(),
157558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                                info.getDeviceId());
157658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        break;
157758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    case TvInputManager.DVB_DEVICE_DVR:
157858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        deviceFileName = String.format("/dev/dvb%d.dvr%d", info.getAdapterId(),
157958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                                info.getDeviceId());
158058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        break;
158158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    case TvInputManager.DVB_DEVICE_FRONTEND:
158258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        deviceFileName = String.format("/dev/dvb%d.frontend%d", info.getAdapterId(),
158358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                                info.getDeviceId());
158458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        break;
158558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    default:
158658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        throw new IllegalArgumentException("Invalid DVB device: " + device);
158758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                }
158858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                try {
158958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    // The DVB frontend device only needs to be opened in read/write mode, which
159058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    // allows performing tuning operations. The DVB demux and DVR device are enough
159158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    // to be opened in read only mode.
159258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    return ParcelFileDescriptor.open(new File(deviceFileName),
159358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                            TvInputManager.DVB_DEVICE_FRONTEND == device
159458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                                    ? ParcelFileDescriptor.MODE_READ_WRITE
159558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                                    : ParcelFileDescriptor.MODE_READ_ONLY);
159658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                } catch (FileNotFoundException e) {
159758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    return null;
159858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                }
159958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            } finally {
160058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                Binder.restoreCallingIdentity(identity);
160158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            }
160258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        }
160358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
160458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        @Override
1605c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
1606c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                throws RemoteException {
1607c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (mContext.checkCallingPermission(
1608c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    android.Manifest.permission.CAPTURE_TV_INPUT)
1609c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    != PackageManager.PERMISSION_GRANTED) {
1610c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1611c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1612c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1613c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final long identity = Binder.clearCallingIdentity();
1614c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final int callingUid = Binder.getCallingUid();
1615c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1616c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    userId, "getAvailableTvStreamConfigList");
1617c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            try {
1618c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return mTvInputHardwareManager.getAvailableTvStreamConfigList(
1619c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        inputId, callingUid, resolvedUserId);
1620c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            } finally {
1621c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Binder.restoreCallingIdentity(identity);
1622c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1623c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
1624c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1625c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        @Override
1626c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
1627c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int userId)
1628c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                throws RemoteException {
1629c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (mContext.checkCallingPermission(
1630c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    android.Manifest.permission.CAPTURE_TV_INPUT)
1631c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    != PackageManager.PERMISSION_GRANTED) {
1632c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1633c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1634c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1635c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final long identity = Binder.clearCallingIdentity();
1636c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final int callingUid = Binder.getCallingUid();
1637c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1638c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    userId, "captureFrame");
1639c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            try {
1640bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                String hardwareInputId = null;
164179124a717c09f12c74d587d3977bf33ca37e6420Terry Heo                synchronized (mLock) {
164279124a717c09f12c74d587d3977bf33ca37e6420Terry Heo                    UserState userState = getUserStateLocked(resolvedUserId);
1643bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    if (userState.inputMap.get(inputId) == null) {
1644fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        Slog.e(TAG, "input not found for " + inputId);
1645bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        return false;
1646bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    }
1647bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    for (SessionState sessionState : userState.sessionStateMap.values()) {
1648fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionState.info.getId().equals(inputId)
1649fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                && sessionState.hardwareSessionToken != null) {
1650bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            hardwareInputId = userState.sessionStateMap.get(
1651fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                    sessionState.hardwareSessionToken).info.getId();
1652bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            break;
1653bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1654bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    }
165579124a717c09f12c74d587d3977bf33ca37e6420Terry Heo                }
1656c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return mTvInputHardwareManager.captureFrame(
1657bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        (hardwareInputId != null) ? hardwareInputId : inputId,
165879124a717c09f12c74d587d3977bf33ca37e6420Terry Heo                        surface, config, callingUid, resolvedUserId);
1659c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            } finally {
1660c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Binder.restoreCallingIdentity(identity);
1661c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1662c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
1663c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1664c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        @Override
1665df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo        public boolean isSingleSessionActive(int userId) throws RemoteException {
1666df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            final long identity = Binder.clearCallingIdentity();
1667df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            final int callingUid = Binder.getCallingUid();
1668df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1669df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    userId, "isSingleSessionActive");
1670df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            try {
1671df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                synchronized (mLock) {
1672df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    UserState userState = getUserStateLocked(resolvedUserId);
1673df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    if (userState.sessionStateMap.size() == 1) {
1674df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                        return true;
167593ff14b7a5d1a9b4d3f57da85b286069fe9d8303Jae Seo                    } else if (userState.sessionStateMap.size() == 2) {
1676df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                        SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
167793ff14b7a5d1a9b4d3f57da85b286069fe9d8303Jae Seo                                new SessionState[2]);
1678df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                        // Check if there is a wrapper input.
1679fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionStates[0].hardwareSessionToken != null
1680fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                || sessionStates[1].hardwareSessionToken != null) {
1681df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                            return true;
1682df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                        }
1683df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    }
1684df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    return false;
1685df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                }
1686df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            } finally {
1687df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                Binder.restoreCallingIdentity(identity);
1688df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            }
1689df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo        }
1690df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo
1691df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo        @Override
16920f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo        @SuppressWarnings("resource")
1693e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1694e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
16950f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1696e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    != PackageManager.PERMISSION_GRANTED) {
16970f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo                pw.println("Permission Denial: can't dump TvInputManager from pid="
16980f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
1699e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                return;
1700e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            }
1701e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1702e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            synchronized (mLock) {
1703e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.println("User Ids (Current user: " + mCurrentUserId + "):");
1704e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.increaseIndent();
1705e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                for (int i = 0; i < mUserStates.size(); i++) {
1706e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    int userId = mUserStates.keyAt(i);
1707e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println(Integer.valueOf(userId));
1708e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                }
1709e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.decreaseIndent();
1710e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1711e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                for (int i = 0; i < mUserStates.size(); i++) {
1712e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    int userId = mUserStates.keyAt(i);
1713e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    UserState userState = getUserStateLocked(userId);
1714e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("UserState (" + userId + "):");
1715e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1716e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1717969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("inputMap: inputId -> TvInputState");
1718e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
17198e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                    for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) {
17208e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                        pw.println(entry.getKey() + ": " + entry.getValue());
1721e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1722e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1723e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1724969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("packageSet:");
1725e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1726969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (String packageName : userState.packageSet) {
1727e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(packageName);
1728e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1729e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1730e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1731e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("clientStateMap: ITvInputClient -> ClientState");
1732e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1733e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    for (Map.Entry<IBinder, ClientState> entry :
1734e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.clientStateMap.entrySet()) {
1735e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        ClientState client = entry.getValue();
1736e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + client);
1737e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1738e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1739e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1740fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("sessionTokens:");
1741e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1742fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        for (IBinder token : client.sessionTokens) {
1743e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            pw.println("" + token);
1744e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        }
1745e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1746e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1747fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("clientTokens: " + client.clientToken);
1748fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("userId: " + client.userId);
1749e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1750e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1751e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1752e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1753e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1754187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    pw.println("serviceStateMap: ComponentName -> ServiceState");
1755e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1756187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    for (Map.Entry<ComponentName, ServiceState> entry :
1757e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.serviceStateMap.entrySet()) {
1758e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        ServiceState service = entry.getValue();
1759e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + service);
1760e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1761e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1762e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1763fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("sessionTokens:");
1764e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1765fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        for (IBinder token : service.sessionTokens) {
1766e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            pw.println("" + token);
1767e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        }
1768e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1769e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1770fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("service: " + service.service);
1771fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("callback: " + service.callback);
1772fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("bound: " + service.bound);
1773fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("reconnecting: " + service.reconnecting);
1774e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1775e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1776e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1777e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1778e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1779e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("sessionStateMap: ITvInputSession -> SessionState");
1780e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1781e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    for (Map.Entry<IBinder, SessionState> entry :
1782e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.sessionStateMap.entrySet()) {
1783e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        SessionState session = entry.getValue();
1784e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + session);
1785e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1786e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1787fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("info: " + session.info);
1788fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("client: " + session.client);
1789fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("seq: " + session.seq);
1790fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("callingUid: " + session.callingUid);
1791fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("userId: " + session.userId);
1792fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("sessionToken: " + session.sessionToken);
1793fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("session: " + session.session);
1794fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("logUri: " + session.logUri);
1795fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
1796e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1797e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1798e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1799e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1800969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("callbackSet:");
1801969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.increaseIndent();
1802969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (ITvInputManagerCallback callback : userState.callbackSet) {
1803969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        pw.println(callback.toString());
1804969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    }
1805969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.decreaseIndent();
1806969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1807956afc2ba79f50bb8025c6d334653e3c3419b480Ji-Hwan Lee                    pw.println("mainSessionToken: " + userState.mainSessionToken);
1808e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1809e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                }
1810e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            }
1811e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        }
18123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
18133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
18143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class UserState {
1815969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A mapping from the TV input id to its TvInputState.
18166e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private Map<String, TvInputState> inputMap = new HashMap<>();
18173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1818969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A set of all TV input packages.
18196e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Set<String> packageSet = new HashSet<>();
18205c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
18219c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        // A list of all TV content rating systems defined.
18229c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        private final List<TvContentRatingSystemInfo>
18236e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                contentRatingSystemList = new ArrayList<>();
18245c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim
182572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // A mapping from the token of a client to its state.
18266e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Map<IBinder, ClientState> clientStateMap = new HashMap<>();
182772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
18283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the name of a TV input service to its state.
18296e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>();
18303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
18313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the token of a TV input session to its state.
18326e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>();
1833969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1834969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A set of callbacks.
18356e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Set<ITvInputManagerCallback> callbackSet = new HashSet<>();
183679124a717c09f12c74d587d3977bf33ca37e6420Terry Heo
18374c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        // The token of a "main" TV input session.
18384c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        private IBinder mainSessionToken = null;
1839783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1840783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        // Persistent data store for all internal settings maintained by the TV input manager
1841783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        // service.
1842783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        private final PersistentDataStore persistentDataStore;
1843783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1844783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        private UserState(Context context, int userId) {
1845783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            persistentDataStore = new PersistentDataStore(context, userId);
1846783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
18473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
18483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
184972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    private final class ClientState implements IBinder.DeathRecipient {
18506e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final List<IBinder> sessionTokens = new ArrayList<>();
185172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
1852fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private IBinder clientToken;
1853fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final int userId;
185472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
185572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState(IBinder clientToken, int userId) {
1856fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.clientToken = clientToken;
1857fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.userId = userId;
185872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
185972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
186072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        public boolean isEmpty() {
1861fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            return sessionTokens.isEmpty();
186272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
186372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
186472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        @Override
186572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        public void binderDied() {
186672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            synchronized (mLock) {
1867fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                UserState userState = getUserStateLocked(userId);
186872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                // DO NOT remove the client state of clientStateMap in this method. It will be
1869a65118e13b5ceb54454b48f67ea754a38a08f27aJi-Hwan Lee                // removed in releaseSessionLocked().
1870fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                ClientState clientState = userState.clientStateMap.get(clientToken);
187172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                if (clientState != null) {
1872fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    while (clientState.sessionTokens.size() > 0) {
187372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        releaseSessionLocked(
1874fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
187572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    }
187672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                }
1877fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                clientToken = null;
187872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
187972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
188072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    }
188172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
18823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceState {
18836e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final List<IBinder> sessionTokens = new ArrayList<>();
1884fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final ServiceConnection connection;
1885fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final ComponentName component;
1886fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final boolean isHardware;
18876e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final List<TvInputInfo> inputList = new ArrayList<>();
18883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1889fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private ITvInputService service;
1890fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private ServiceCallback callback;
1891fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private boolean bound;
1892fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private boolean reconnecting;
18933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
18949e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        private ServiceState(ComponentName component, int userId) {
1895fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.component = component;
1896fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.connection = new InputServiceConnection(component, userId);
1897fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
1898fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
1899fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    }
1900fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
1901fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private static final class TvInputState {
1902fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        // A TvInputInfo object which represents the TV input.
1903fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private TvInputInfo info;
1904fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
1905fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        // The state of TV input. Connected by default.
1906fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private int state = INPUT_STATE_CONNECTED;
1907fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
1908fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
1909fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public String toString() {
1910fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            return "info: " + info + "; state: " + state;
19113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
19123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
19133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
19142b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private final class SessionState implements IBinder.DeathRecipient {
1915fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final TvInputInfo info;
1916fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final ITvInputClient client;
1917fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final int seq;
1918fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final int callingUid;
1919fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final int userId;
1920fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final IBinder sessionToken;
1921fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private ITvInputSession session;
1922fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private Uri logUri;
1923bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        // Not null if this session represents an external device connected to a hardware TV input.
1924fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private IBinder hardwareSessionToken;
19253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1926bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
1927bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                int seq, int callingUid, int userId) {
1928fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.sessionToken = sessionToken;
1929fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.info = info;
1930fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.client = client;
1931fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.seq = seq;
1932fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.callingUid = callingUid;
1933fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.userId = userId;
19342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
19352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
19362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        @Override
19372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void binderDied() {
19382b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
1939fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                session = null;
1940fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (client != null) {
19412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    try {
1942fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        client.onSessionReleased(seq);
19432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    } catch(RemoteException e) {
19442b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        Slog.e(TAG, "error in onSessionReleased", e);
19452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
19462b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
1947bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                // If there are any other sessions based on this session, they should be released.
1948fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                UserState userState = getUserStateLocked(userId);
1949bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                for (SessionState sessionState : userState.sessionStateMap.values()) {
1950fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (sessionToken == sessionState.hardwareSessionToken) {
1951fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID,
1952fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                userId);
1953bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        try {
1954fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            sessionState.client.onSessionReleased(sessionState.seq);
1955bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        } catch (RemoteException e) {
1956bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            Slog.e(TAG, "error in onSessionReleased", e);
1957bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1958bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    }
1959bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                }
1960fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                removeSessionStateLocked(sessionToken, userId);
19612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
19623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
19633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
19643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
19653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class InputServiceConnection implements ServiceConnection {
19669e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        private final ComponentName mComponent;
19673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
19683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
19699e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        private InputServiceConnection(ComponentName component, int userId) {
19709e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            mComponent = component;
19713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
19723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
19733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
19743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
19759e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        public void onServiceConnected(ComponentName component, IBinder service) {
19763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
19779e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                Slog.d(TAG, "onServiceConnected(component=" + component + ")");
19783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
19793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
1980969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                UserState userState = getUserStateLocked(mUserId);
19819e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                ServiceState serviceState = userState.serviceStateMap.get(mComponent);
1982fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                serviceState.service = ITvInputService.Stub.asInterface(service);
19833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
19843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // Register a callback, if we need to.
1985fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (serviceState.isHardware && serviceState.callback == null) {
1986fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.callback = new ServiceCallback(mComponent, mUserId);
19873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1988fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.registerCallback(serviceState.callback);
19893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
19909a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in registerCallback", e);
19913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
19923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
19933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
19943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // And create sessions, if any.
1995fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                for (IBinder sessionToken : serviceState.sessionTokens) {
1996fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
19973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1998969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1999187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                for (TvInputState inputState : userState.inputMap.values()) {
2000fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (inputState.info.getComponent().equals(component)
200182fce64530d19a4da1c02d424fb2515feafe6a70Jae Seo                            && inputState.state != INPUT_STATE_CONNECTED) {
2002fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        notifyInputStateChangedLocked(userState, inputState.info.getId(),
2003fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                inputState.state, null);
2004187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2005187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
2006187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2007fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (serviceState.isHardware) {
20084f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    List<TvInputHardwareInfo> hardwareInfoList =
20094f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                            mTvInputHardwareManager.getHardwareList();
2010187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
2011187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        try {
2012fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            serviceState.service.notifyHardwareAdded(hardwareInfo);
2013187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        } catch (RemoteException e) {
2014187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                            Slog.e(TAG, "error in notifyHardwareAdded", e);
2015187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        }
2016187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2017187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2018546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    List<HdmiDeviceInfo> deviceInfoList =
2019546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                            mTvInputHardwareManager.getHdmiDeviceList();
2020546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
20214f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        try {
2022fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
20234f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        } catch (RemoteException e) {
2024546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                            Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
20254f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        }
20264f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    }
2027969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
20283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
20293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
20303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
20313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
20329e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        public void onServiceDisconnected(ComponentName component) {
20333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
20349e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                Slog.d(TAG, "onServiceDisconnected(component=" + component + ")");
20353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
20369e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            if (!mComponent.equals(component)) {
20372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                throw new IllegalArgumentException("Mismatched ComponentName: "
20389e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                        + mComponent + " (expected), " + component + " (actual).");
20392b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
20402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
20412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                UserState userState = getUserStateLocked(mUserId);
20429e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                ServiceState serviceState = userState.serviceStateMap.get(mComponent);
20432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState != null) {
2044fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.reconnecting = true;
2045fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.bound = false;
2046fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.service = null;
2047fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.callback = null;
20482b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
2049426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
20502b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
20512b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
20523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
20533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
20543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
20553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
20569e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        private final ComponentName mComponent;
20573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
20583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
20599e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        ServiceCallback(ComponentName component, int userId) {
20609e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            mComponent = component;
20613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
20623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
20633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
20644f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        private void ensureHardwarePermission() {
20654f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
20664f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    != PackageManager.PERMISSION_GRANTED) {
20674f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new SecurityException("The caller does not have hardware permission");
20684f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
20694f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
20704f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
20714f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        private void ensureValidInput(TvInputInfo inputInfo) {
20729e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) {
20734f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new IllegalArgumentException("Invalid TvInputInfo");
20744f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
20754f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
20764f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
20774f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        private void addTvInputLocked(TvInputInfo inputInfo) {
20789e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
2079fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            serviceState.inputList.add(inputInfo);
208019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            buildTvInputListLocked(mUserId, null);
20814f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
20824f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
20833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
20844f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
20854f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureHardwarePermission();
20864f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureValidInput(inputInfo);
2087187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
20884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo);
20894f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                addTvInputLocked(inputInfo);
20904f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
20914f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2092187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
20934f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        @Override
20948960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        public void addHdmiTvInput(int id, TvInputInfo inputInfo) {
20954f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureHardwarePermission();
20964f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureValidInput(inputInfo);
20974f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            synchronized (mLock) {
20988960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                mTvInputHardwareManager.addHdmiTvInput(id, inputInfo);
20994f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                addTvInputLocked(inputInfo);
21003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2101187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
2102187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2103187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2104187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void removeTvInput(String inputId) {
21054f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureHardwarePermission();
21063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
21079e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
2108187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                boolean removed = false;
2109fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                for (Iterator<TvInputInfo> it = serviceState.inputList.iterator();
2110187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        it.hasNext(); ) {
2111187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    if (it.next().getId().equals(inputId)) {
2112187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        it.remove();
2113187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        removed = true;
2114187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        break;
2115187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2116187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
2117187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                if (removed) {
211819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    buildTvInputListLocked(mUserId, null);
21194f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mTvInputHardwareManager.removeTvInput(inputId);
2120187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                } else {
2121fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    Slog.e(TAG, "failed to remove input " + inputId);
2122187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
21233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
21243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
21253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
212631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
2127fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private final class SessionCallback extends ITvInputSessionCallback.Stub {
21289c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee        private final SessionState mSessionState;
2129fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final InputChannel[] mChannels;
2130fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2131fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        SessionCallback(SessionState sessionState, InputChannel[] channels) {
21329c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            mSessionState = sessionState;
2133fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            mChannels = channels;
2134fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2135fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2136fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
21376e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) {
2138fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            if (DEBUG) {
21399c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.info.getId() + ")");
2140fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2141fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
21429c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                mSessionState.session = session;
21436e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                mSessionState.hardwareSessionToken = hardwareSessionToken;
21449c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (session != null && addSessionTokenToClientStateLocked(session)) {
21459c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    sendSessionTokenToClientLocked(mSessionState.client,
21469c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                            mSessionState.info.getId(), mSessionState.sessionToken, mChannels[0],
21479c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                            mSessionState.seq);
2148fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } else {
21499c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId);
21509c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    sendSessionTokenToClientLocked(mSessionState.client,
21519c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                            mSessionState.info.getId(), null, null, mSessionState.seq);
21529c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                }
21539c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                mChannels[0].dispose();
21549c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            }
21559c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee        }
2156fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
21579c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee        private boolean addSessionTokenToClientStateLocked(ITvInputSession session) {
21589c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            try {
21599c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                session.asBinder().linkToDeath(mSessionState, 0);
21609c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            } catch (RemoteException e) {
21619c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                Slog.e(TAG, "session process has already died", e);
21629c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                return false;
21639c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            }
2164fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
21659c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            IBinder clientToken = mSessionState.client.asBinder();
21669c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            UserState userState = getUserStateLocked(mSessionState.userId);
21679c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            ClientState clientState = userState.clientStateMap.get(clientToken);
21689c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            if (clientState == null) {
21699c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                clientState = new ClientState(clientToken, mSessionState.userId);
21709c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                try {
21719c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    clientToken.linkToDeath(clientState, 0);
21729c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                } catch (RemoteException e) {
21739c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    Slog.e(TAG, "client process has already died", e);
21749c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    return false;
2175fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
21769c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                userState.clientStateMap.put(clientToken, clientState);
2177fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
21789c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            clientState.sessionTokens.add(mSessionState.sessionToken);
21799c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            return true;
2180fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2181fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2182fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2183fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onChannelRetuned(Uri channelUri) {
2184fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2185fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2186fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
2187fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
21889c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2189fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2190fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2191fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
2192fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    // TODO: Consider adding this channel change in the watch log. When we do
2193fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    // that, how we can protect the watch log from malicious tv inputs should
2194fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    // be addressed. e.g. add a field which represents where the channel change
2195fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    // originated from.
21969c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq);
2197fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2198fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onChannelRetuned", e);
2199fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2200fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2201fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2202fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2203fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2204fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onTracksChanged(List<TvTrackInfo> tracks) {
2205fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2206fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2207fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onTracksChanged(" + tracks + ")");
2208fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
22099c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2210fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2211fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2212fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
22139c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onTracksChanged(tracks, mSessionState.seq);
2214fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2215fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onTracksChanged", e);
2216fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2217fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2218fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2219fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2220fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2221fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onTrackSelected(int type, String trackId) {
2222fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2223fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2224fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
2225fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
22269c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2227fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2228fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2229fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
22309c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq);
2231fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2232fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onTrackSelected", e);
2233fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2234fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2235fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2236fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2237fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2238fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onVideoAvailable() {
2239fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2240fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2241fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onVideoAvailable()");
2242fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
22439c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2244fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2245fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2246fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
22479c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onVideoAvailable(mSessionState.seq);
2248fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2249fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onVideoAvailable", e);
2250fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2251fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2252fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2253fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2254fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2255fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onVideoUnavailable(int reason) {
2256fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2257fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2258fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
2259fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
22609c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2261fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2262fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2263fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
22649c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
2265fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2266fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onVideoUnavailable", e);
2267fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2268fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2269fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2270fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2271fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2272fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onContentAllowed() {
2273fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2274fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2275fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onContentAllowed()");
2276fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
22779c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2278fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2279fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2280fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
22819c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onContentAllowed(mSessionState.seq);
2282fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2283fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onContentAllowed", e);
2284fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2285fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2286fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2287fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2288fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2289fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onContentBlocked(String rating) {
2290fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2291fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2292fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onContentBlocked()");
2293fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
22949c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2295fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2296fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2297fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
22989c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onContentBlocked(rating, mSessionState.seq);
2299fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2300fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onContentBlocked", e);
2301fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2302fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2303fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2304fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2305fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2306fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onLayoutSurface(int left, int top, int right, int bottom) {
2307fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2308fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2309fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
2310fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            + ", right=" + right + ", bottom=" + bottom + ",)");
2311fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
23129c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2313fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2314fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2315fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
23169c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onLayoutSurface(left, top, right, bottom,
23179c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                            mSessionState.seq);
2318fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2319fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onLayoutSurface", e);
2320fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2321fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2322fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2323fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2324fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2325fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onSessionEvent(String eventType, Bundle eventArgs) {
2326fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2327fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2328fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
2329fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
23309c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2331fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2332fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2333fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
23349c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq);
2335fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2336fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onSessionEvent", e);
2337fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2338fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2339fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
23406f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
23416f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
23426f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftStatusChanged(int status) {
23436f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            synchronized (mLock) {
23446f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (DEBUG) {
23456f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.d(TAG, "onTimeShiftStatusChanged()");
23466f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23476f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (mSessionState.session == null || mSessionState.client == null) {
23486f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    return;
23496f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23506f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                try {
23516f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq);
23526f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                } catch (RemoteException e) {
23536f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.e(TAG, "error in onTimeShiftStatusChanged", e);
23546f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23556f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
23566f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
23576f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
23586f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
23596f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftStartPositionChanged(long timeMs) {
23606f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            synchronized (mLock) {
23616f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (DEBUG) {
23626f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.d(TAG, "onTimeShiftStartPositionChanged()");
23636f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23646f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (mSessionState.session == null || mSessionState.client == null) {
23656f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    return;
23666f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23676f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                try {
23686f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq);
23696f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                } catch (RemoteException e) {
23706f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e);
23716f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23726f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
23736f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
23746f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
23756f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
23766f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftCurrentPositionChanged(long timeMs) {
23776f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            synchronized (mLock) {
23786f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (DEBUG) {
23796f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.d(TAG, "onTimeShiftCurrentPositionChanged()");
23806f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23816f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (mSessionState.session == null || mSessionState.client == null) {
23826f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    return;
23836f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23846f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                try {
23856f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs,
23866f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                            mSessionState.seq);
23876f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                } catch (RemoteException e) {
23886f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e);
23896f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
23906f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
23916f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
2392fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    }
2393fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2394fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private static final class WatchLogHandler extends Handler {
23957eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // There are only two kinds of watch events that can happen on the system:
23967eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // 1. The current TV input session is tuned to a new channel.
23977eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // 2. The session is released for some reason.
23987eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // The former indicates the end of the previous log entry, if any, followed by the start of
23997eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // a new entry. The latter indicates the end of the most recent entry for the given session.
24007eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // Here the system supplies the database the smallest set of information only that is
24017eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // sufficient to consolidate the log entries while minimizing database operations in the
24027eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // system service.
24038c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        static final int MSG_LOG_WATCH_START = 1;
24048c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        static final int MSG_LOG_WATCH_END = 2;
24058c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        static final int MSG_SWITCH_CONTENT_RESOLVER = 3;
24067eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
24078c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        private ContentResolver mContentResolver;
2408fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
24098c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        WatchLogHandler(ContentResolver contentResolver, Looper looper) {
241031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            super(looper);
2411fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            mContentResolver = contentResolver;
241231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
241331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
241431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        @Override
241531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public void handleMessage(Message msg) {
241631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            switch (msg.what) {
24177eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                case MSG_LOG_WATCH_START: {
241831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
24197eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    String packageName = (String) args.arg1;
24207eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    long watchStartTime = (long) args.arg2;
24217eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    long channelId = (long) args.arg3;
24227eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    Bundle tuneParams = (Bundle) args.arg4;
24237eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    IBinder sessionToken = (IBinder) args.arg5;
24247eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
24257eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    ContentValues values = new ContentValues();
24267eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
24277eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
24287eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                            watchStartTime);
24297eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
24307eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    if (tuneParams != null) {
24317eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS,
24327eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                                encodeTuneParams(tuneParams));
24337eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    }
24347eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
24357eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                            sessionToken.toString());
24367eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
24377eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
243831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
24398c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    break;
244031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
24417eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                case MSG_LOG_WATCH_END: {
244231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
24437eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    IBinder sessionToken = (IBinder) args.arg1;
24447eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    long watchEndTime = (long) args.arg2;
24457eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
24467eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    ContentValues values = new ContentValues();
24477eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
24487eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                            watchEndTime);
24497eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
24507eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                            sessionToken.toString());
24517eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
24527eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
245331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
24548c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    break;
24558c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                }
24568c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                case MSG_SWITCH_CONTENT_RESOLVER: {
24578c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    mContentResolver = (ContentResolver) msg.obj;
24588c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    break;
245931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
246031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                default: {
24618c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    Slog.w(TAG, "unhandled message code: " + msg.what);
24628c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    break;
246331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
246431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
246531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
246631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
24677eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        private String encodeTuneParams(Bundle tuneParams) {
24687eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            StringBuilder builder = new StringBuilder();
24697eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            Set<String> keySet = tuneParams.keySet();
24707eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            Iterator<String> it = keySet.iterator();
24717eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            while (it.hasNext()) {
24727eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                String key = it.next();
24737eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                Object value = tuneParams.get(key);
24747eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                if (value == null) {
24757eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    continue;
247631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
24777eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                builder.append(replaceEscapeCharacters(key));
24787eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                builder.append("=");
24797eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                builder.append(replaceEscapeCharacters(value.toString()));
24807eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                if (it.hasNext()) {
24817eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    builder.append(", ");
248231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
248331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
24847eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            return builder.toString();
248531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
248631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
24877eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        private String replaceEscapeCharacters(String src) {
24887eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            final char ESCAPE_CHARACTER = '%';
24897eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            final String ENCODING_TARGET_CHARACTERS = "%=,";
24907eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            StringBuilder builder = new StringBuilder();
24917eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            for (char ch : src.toCharArray()) {
24927eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) {
24937eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    builder.append(ESCAPE_CHARACTER);
2494579befecb248162021929ab58ffd23f1724cc6beJae Seo                }
24957eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                builder.append(ch);
2496579befecb248162021929ab58ffd23f1724cc6beJae Seo            }
24977eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            return builder.toString();
2498579befecb248162021929ab58ffd23f1724cc6beJae Seo        }
249931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    }
2500969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2501fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private final class HardwareListener implements TvInputHardwareManager.Listener {
2502187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2503187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onStateChanged(String inputId, int state) {
2504969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            synchronized (mLock) {
2505969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                setStateLocked(inputId, state, mCurrentUserId);
2506969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2507969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
2508187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2509187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2510187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
2511187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
2512187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                UserState userState = getUserStateLocked(mCurrentUserId);
2513187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                // Broadcast the event to all hardware inputs.
2514187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2515fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (!serviceState.isHardware || serviceState.service == null) continue;
2516187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    try {
2517fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.notifyHardwareAdded(info);
2518187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    } catch (RemoteException e) {
2519187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        Slog.e(TAG, "error in notifyHardwareAdded", e);
2520187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2521187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
2522187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
2523187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
2524187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2525187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
25264f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
2527187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
2528187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                UserState userState = getUserStateLocked(mCurrentUserId);
2529187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                // Broadcast the event to all hardware inputs.
2530187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2531fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (!serviceState.isHardware || serviceState.service == null) continue;
2532187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    try {
2533fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.notifyHardwareRemoved(info);
2534187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    } catch (RemoteException e) {
2535187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        Slog.e(TAG, "error in notifyHardwareRemoved", e);
2536187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2537187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
2538187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
2539187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
2540187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2541187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2542546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
2543187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
25444f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                UserState userState = getUserStateLocked(mCurrentUserId);
25454f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                // Broadcast the event to all hardware inputs.
25464f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2547fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (!serviceState.isHardware || serviceState.service == null) continue;
25484f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    try {
2549fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
25504f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    } catch (RemoteException e) {
2551546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
25524f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    }
25534f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                }
2554187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
2555187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
2556187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2557187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2558546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
2559187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
25604f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                UserState userState = getUserStateLocked(mCurrentUserId);
25614f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                // Broadcast the event to all hardware inputs.
25624f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2563fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (!serviceState.isHardware || serviceState.service == null) continue;
25644f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    try {
2565fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
25664f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    } catch (RemoteException e) {
2567546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
25684f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    }
25694f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                }
2570187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
2571187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
257261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang
257361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        @Override
2574e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim        public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
2575e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim            synchronized (mLock) {
25766e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                Integer state;
2577e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                switch (deviceInfo.getDevicePowerStatus()) {
2578e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_ON:
2579e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        state = INPUT_STATE_CONNECTED;
2580e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        break;
2581e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_STANDBY:
2582e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
2583e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
2584e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        state = INPUT_STATE_CONNECTED_STANDBY;
2585e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        break;
2586e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_UNKNOWN:
2587e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    default:
2588e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        state = null;
2589e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        break;
2590e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                }
2591e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                if (state != null) {
25926e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    setStateLocked(inputId, state, mCurrentUserId);
2593e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                }
2594e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim            }
259561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        }
2596969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
2597fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang
2598fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang    private static class SessionNotFoundException extends IllegalArgumentException {
2599fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        public SessionNotFoundException(String name) {
2600fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            super(name);
2601fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        }
2602fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang    }
26033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
2604