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
224eee6a73e476cd2d82a69f3a535628901047f140Jae Seoimport android.annotation.Nullable;
233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.app.ActivityManager;
243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.BroadcastReceiver;
253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.ComponentName;
2631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentResolver;
2731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentUris;
2831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentValues;
293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Context;
303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Intent;
313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.IntentFilter;
323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.ServiceConnection;
339c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seoimport android.content.pm.ActivityInfo;
343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.PackageManager;
358c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seoimport android.content.pm.PackageManager.NameNotFoundException;
363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.ResolveInfo;
373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.ServiceInfo;
389a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.graphics.Rect;
39e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kimimport android.hardware.hdmi.HdmiControlManager;
4061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
414b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seoimport android.media.PlaybackParams;
4258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport android.media.tv.DvbDeviceInfo;
43d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputClient;
44d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
45d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
46d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputManager;
47969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.ITvInputManagerCallback;
48d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputService;
49d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputServiceCallback;
50d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputSession;
51d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputSessionCallback;
52783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.media.tv.TvContentRating;
539c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seoimport android.media.tv.TvContentRatingSystemInfo;
54d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvContract;
55d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputHardwareInfo;
56d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputInfo;
579c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seoimport android.media.tv.TvInputManager;
58d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputService;
59c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heoimport android.media.tv.TvStreamConfig;
601f213914c45c23c653f721690da2ce0718e63139Dongwon Kangimport android.media.tv.TvTrackInfo;
613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.net.Uri;
623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Binder;
63832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Choimport android.os.Bundle;
6431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Handler;
653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.IBinder;
6631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Looper;
6731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Message;
6858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport android.os.ParcelFileDescriptor;
693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Process;
703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.RemoteException;
713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.UserHandle;
72c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seoimport android.text.TextUtils;
739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.util.Slog;
743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.util.SparseArray;
756a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputChannel;
763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.view.Surface;
773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.internal.content.PackageMonitor;
7931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.internal.os.SomeArgs;
80fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkeyimport com.android.internal.util.DumpUtils;
81e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport com.android.internal.util.IndentingPrintWriter;
8231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.server.IoThread;
833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.server.SystemService;
843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.io.File;
86e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport java.io.FileDescriptor;
8758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.io.FileNotFoundException;
88e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport java.io.PrintWriter;
893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.ArrayList;
9019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Leeimport java.util.Arrays;
9158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.util.Collections;
923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.HashMap;
935c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport java.util.HashSet;
94187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport java.util.Iterator;
953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.List;
963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.Map;
975c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport java.util.Set;
9858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.util.regex.Matcher;
9958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chungimport java.util.regex.Pattern;
1003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo/** This class provides a system service that manages television inputs. */
1023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seopublic final class TvInputManagerService extends SystemService {
103ee2ec05ed7c0d3cb9115f4ddd7c3613269c4a57bJae Seo    private static final boolean DEBUG = false;
1043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final String TAG = "TvInputManagerService";
105ef1659f6249205e052c14fb607385b0e6b654223Jiabin    private static final String DVB_DIRECTORY = "/dev/dvb";
1063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
107ef1659f6249205e052c14fb607385b0e6b654223Jiabin    // There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
108ef1659f6249205e052c14fb607385b0e6b654223Jiabin    // another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
109ef1659f6249205e052c14fb607385b0e6b654223Jiabin    // DVB frontend devices from the list of files in the /dev and /dev/dvb/adapter%d directory.
11058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung    private static final Pattern sFrontEndDevicePattern =
11158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$");
112ef1659f6249205e052c14fb607385b0e6b654223Jiabin    private static final Pattern sAdapterDirPattern =
113ef1659f6249205e052c14fb607385b0e6b654223Jiabin            Pattern.compile("^adapter([0-9]+)$");
114ef1659f6249205e052c14fb607385b0e6b654223Jiabin    private static final Pattern sFrontEndInAdapterDirPattern =
115ef1659f6249205e052c14fb607385b0e6b654223Jiabin            Pattern.compile("^frontend([0-9]+)$");
11658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
1173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Context mContext;
118c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHardwareManager mTvInputHardwareManager;
1193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A global lock.
1213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Object mLock = new Object();
1223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // ID of the current user.
124233d94c0df13a7e54f738f442457cebc62294384Xiaohui Chen    private int mCurrentUserId = UserHandle.USER_SYSTEM;
1253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A map from user id to UserState.
1276e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo    private final SparseArray<UserState> mUserStates = new SparseArray<>();
1283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1297eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo    private final WatchLogHandler mWatchLogHandler;
13031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public TvInputManagerService(Context context) {
1323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        super(context);
13331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext = context;
1358c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        mWatchLogHandler = new WatchLogHandler(mContext.getContentResolver(),
1368c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                IoThread.get().getLooper());
137187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
13831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1404f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo            getOrCreateUserStateLocked(mCurrentUserId);
1413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    @Override
1453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public void onStart() {
1463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
1473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1490ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    @Override
1500ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    public void onBootPhase(int phase) {
1510ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1520ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            registerBroadcastReceivers();
153187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1540ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            synchronized (mLock) {
15519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                buildTvInputListLocked(mCurrentUserId, null);
1569c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                buildTvContentRatingSystemListLocked(mCurrentUserId);
1570ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            }
1580ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee        }
159969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        mTvInputHardwareManager.onBootPhase(phase);
1600ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    }
1610ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee
16219c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho    @Override
16319c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho    public void onUnlockUser(int userHandle) {
16419c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho        if (DEBUG) Slog.d(TAG, "onUnlockUser(userHandle=" + userHandle + ")");
16519c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho        synchronized (mLock) {
16619c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho            if (mCurrentUserId != userHandle) {
16719c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho                return;
16819c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho            }
16919c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho            buildTvInputListLocked(mCurrentUserId, null);
17019c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho            buildTvContentRatingSystemListLocked(mCurrentUserId);
17119c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho        }
17219c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho    }
17319c47d79951eb420e6926d188c7d39f5563c5b5fYoungsang Cho
1743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void registerBroadcastReceivers() {
1753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageMonitor monitor = new PackageMonitor() {
17619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            private void buildTvInputList(String[] packages) {
17719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                synchronized (mLock) {
178a897e5f5e32557efa03c375fde17faae3282673fDongwon Kang                    if (mCurrentUserId == getChangingUserId()) {
179a897e5f5e32557efa03c375fde17faae3282673fDongwon Kang                        buildTvInputListLocked(mCurrentUserId, packages);
180a897e5f5e32557efa03c375fde17faae3282673fDongwon Kang                        buildTvContentRatingSystemListLocked(mCurrentUserId);
181a897e5f5e32557efa03c375fde17faae3282673fDongwon Kang                    }
18219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
18319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
18419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
18519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            @Override
18619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            public void onPackageUpdateFinished(String packageName, int uid) {
18719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
18819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // This callback is invoked when the TV input is reinstalled.
18919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // In this case, isReplacing() always returns true.
19019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                buildTvInputList(new String[] { packageName });
19119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
19219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
19319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            @Override
19419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            public void onPackagesAvailable(String[] packages) {
19519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (DEBUG) {
19619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
19719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
19819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // This callback is invoked when the media on which some packages exist become
19919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // available.
20019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (isReplacing()) {
20119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    buildTvInputList(packages);
20219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
20319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
20419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
20519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            @Override
20619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            public void onPackagesUnavailable(String[] packages) {
20719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // This callback is invoked when the media on which some packages exist become
20819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // unavailable.
20919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (DEBUG)  {
21019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
21119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                            + ")");
21219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
21319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (isReplacing()) {
21419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    buildTvInputList(packages);
21519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
21619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
21719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
2183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
2193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSomePackagesChanged() {
22019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
22119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // the TV inputs.
222426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
22319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                if (isReplacing()) {
22419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
22519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    // When the package is updated, buildTvInputListLocked is called in other
22619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    // methods instead.
22719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    return;
2283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
22919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                buildTvInputList(null);
2303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2315c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
2325c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            @Override
23331a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang            public boolean onPackageChanged(String packageName, int uid, String[] components) {
23431a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang                // The input list needs to be updated in any cases, regardless of whether
23531a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang                // it happened to the whole package or a specific component. Returning true so that
23631a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang                // the update can be handled in {@link #onSomePackagesChanged}.
23731a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang                return true;
23831a8f8400b6e612bd287cc8ca3c2fcba0edd816eDongwon Kang            }
2393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
2403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        monitor.register(mContext, null, UserHandle.ALL, true);
2413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        IntentFilter intentFilter = new IntentFilter();
2433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
2443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
2453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext.registerReceiverAsUser(new BroadcastReceiver() {
2463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
2473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onReceive(Context context, Intent intent) {
2483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                String action = intent.getAction();
2493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
2503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
2513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
2523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
2533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }, UserHandle.ALL, intentFilter, null, null);
2563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2589e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee    private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) {
259187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE,
2609e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
261187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
262187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
26319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee    private void buildTvInputListLocked(int userId, String[] updatedPackages) {
2644f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
265969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        userState.packageSet.clear();
2663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
26719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        if (DEBUG) Slog.d(TAG, "buildTvInputList");
2683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageManager pm = mContext.getPackageManager();
26976976fae6fbe2f30ba209575557da153e29be33bJae Seo        List<ResolveInfo> services = pm.queryIntentServicesAsUser(
270e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                new Intent(TvInputService.SERVICE_INTERFACE),
27176976fae6fbe2f30ba209575557da153e29be33bJae Seo                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
27276976fae6fbe2f30ba209575557da153e29be33bJae Seo                userId);
2736e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        List<TvInputInfo> inputList = new ArrayList<>();
2743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        for (ResolveInfo ri : services) {
2753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            ServiceInfo si = ri.serviceInfo;
2763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
2779a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
2783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + android.Manifest.permission.BIND_TV_INPUT);
2793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                continue;
2803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2819cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo
2829cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            ComponentName component = new ComponentName(si.packageName, si.name);
2839cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            if (hasHardwarePermission(pm, component)) {
2849cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                ServiceState serviceState = userState.serviceStateMap.get(component);
2859cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                if (serviceState == null) {
286c980f43d940f1154a15ef003fb00c19708c396daJae Seo                    // New hardware input found. Create a new ServiceState and connect to the
287c980f43d940f1154a15ef003fb00c19708c396daJae Seo                    // service to populate the hardware list.
2889cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    serviceState = new ServiceState(component, userId);
2899cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    userState.serviceStateMap.put(component, serviceState);
290f271eacba7997d2751c336153634fac53bc4d660Wonsik Kim                    updateServiceConnectionLocked(component, userId);
291187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                } else {
29271d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    inputList.addAll(serviceState.hardwareInputMap.values());
293187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
2949cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            } else {
2959cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                try {
296c03671fdf628a713b984483801554d889e54ae23Jae Seo                    TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
297c03671fdf628a713b984483801554d889e54ae23Jae Seo                    inputList.add(info);
29818c0cfb0750668daf8b5c099122ea4fb214a1449Jae Seo                } catch (Exception e) {
299fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    Slog.e(TAG, "failed to load TV input " + si.name, e);
3009cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo                    continue;
301969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
3029cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            }
3039cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo            userState.packageSet.add(si.packageName);
3049cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo        }
305187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
3066e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        Map<String, TvInputState> inputMap = new HashMap<>();
3079cc28e5175e1391646b29469d329c9c1c9311ee1Jae Seo        for (TvInputInfo info : inputList) {
308fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            if (DEBUG) {
309fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.d(TAG, "add " + info.getId());
310fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            }
311abda420b9d2959776093230cd2e157a6080f2f64Jae Seo            TvInputState inputState = userState.inputMap.get(info.getId());
312abda420b9d2959776093230cd2e157a6080f2f64Jae Seo            if (inputState == null) {
313abda420b9d2959776093230cd2e157a6080f2f64Jae Seo                inputState = new TvInputState();
314e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            }
3152263fb0b1a58282fb5a0aecb4c6b4e77296506b7Shubang            inputState.info = info;
316abda420b9d2959776093230cd2e157a6080f2f64Jae Seo            inputMap.put(info.getId(), inputState);
3173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3188e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
3198e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        for (String inputId : inputMap.keySet()) {
3208e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            if (!userState.inputMap.containsKey(inputId)) {
3218e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                notifyInputAddedLocked(userState, inputId);
32219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            } else if (updatedPackages != null) {
32319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                // Notify the package updates
3249c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                ComponentName component = inputMap.get(inputId).info.getComponent();
32519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                for (String updatedPackage : updatedPackages) {
3269c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    if (component.getPackageName().equals(updatedPackage)) {
3279c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                        updateServiceConnectionLocked(component, userId);
32819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                        notifyInputUpdatedLocked(userState, inputId);
32919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                        break;
33019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    }
33119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                }
3328e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            }
3338e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
3348e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
3358e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        for (String inputId : userState.inputMap.keySet()) {
3368e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            if (!inputMap.containsKey(inputId)) {
337fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                TvInputInfo info = userState.inputMap.get(inputId).info;
338426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
339426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                if (serviceState != null) {
340426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
341426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                }
3428e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                notifyInputRemovedLocked(userState, inputId);
3438e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            }
3448e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
3458e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
3468e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        userState.inputMap.clear();
3478e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        userState.inputMap = inputMap;
3489c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo    }
3499c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo
3509c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo    private void buildTvContentRatingSystemListLocked(int userId) {
3514f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
3529c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        userState.contentRatingSystemList.clear();
3539c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo
3549c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        final PackageManager pm = mContext.getPackageManager();
3559c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS);
3569c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        for (ResolveInfo resolveInfo :
3579c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) {
3589c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            ActivityInfo receiver = resolveInfo.activityInfo;
3599c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            Bundle metaData = receiver.metaData;
3609c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            if (metaData == null) {
3619c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                continue;
3629c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            }
3635c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim
3649c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS);
3659c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            if (xmlResId == 0) {
3669c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                Slog.w(TAG, "Missing meta-data '"
3679c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                        + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver "
3689c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                        + receiver.packageName + "/" + receiver.name);
3699c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                continue;
3705c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            }
3719c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            userState.contentRatingSystemList.add(
3729c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                    TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId,
3739c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                            receiver.applicationInfo));
3745c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim        }
3753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void switchUser(int userId) {
3783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
3793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (mCurrentUserId == userId) {
3803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
3813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
3828049f20df8c10397aac8d31bcb455913ad02bb67shubang            UserState userState = mUserStates.get(mCurrentUserId);
3838049f20df8c10397aac8d31bcb455913ad02bb67shubang            List<SessionState> sessionStatesToRelease = new ArrayList<>();
3848049f20df8c10397aac8d31bcb455913ad02bb67shubang            for (SessionState sessionState : userState.sessionStateMap.values()) {
3858049f20df8c10397aac8d31bcb455913ad02bb67shubang                if (sessionState.session != null && !sessionState.isRecordingSession) {
3868049f20df8c10397aac8d31bcb455913ad02bb67shubang                    sessionStatesToRelease.add(sessionState);
3878049f20df8c10397aac8d31bcb455913ad02bb67shubang                }
3888049f20df8c10397aac8d31bcb455913ad02bb67shubang            }
3898049f20df8c10397aac8d31bcb455913ad02bb67shubang            for (SessionState sessionState : sessionStatesToRelease) {
3908049f20df8c10397aac8d31bcb455913ad02bb67shubang                try {
3918049f20df8c10397aac8d31bcb455913ad02bb67shubang                    sessionState.session.release();
3928049f20df8c10397aac8d31bcb455913ad02bb67shubang                } catch (RemoteException e) {
3938049f20df8c10397aac8d31bcb455913ad02bb67shubang                    Slog.e(TAG, "error in release", e);
3948049f20df8c10397aac8d31bcb455913ad02bb67shubang                }
3958049f20df8c10397aac8d31bcb455913ad02bb67shubang                clearSessionAndNotifyClientLocked(sessionState);
3968049f20df8c10397aac8d31bcb455913ad02bb67shubang            }
3978049f20df8c10397aac8d31bcb455913ad02bb67shubang
3988049f20df8c10397aac8d31bcb455913ad02bb67shubang            for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator();
3998049f20df8c10397aac8d31bcb455913ad02bb67shubang                 it.hasNext(); ) {
4008049f20df8c10397aac8d31bcb455913ad02bb67shubang                ComponentName component = it.next();
4018049f20df8c10397aac8d31bcb455913ad02bb67shubang                ServiceState serviceState = userState.serviceStateMap.get(component);
4028049f20df8c10397aac8d31bcb455913ad02bb67shubang                if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
4038049f20df8c10397aac8d31bcb455913ad02bb67shubang                    if (serviceState.callback != null) {
4048049f20df8c10397aac8d31bcb455913ad02bb67shubang                        try {
4058049f20df8c10397aac8d31bcb455913ad02bb67shubang                            serviceState.service.unregisterCallback(serviceState.callback);
4068049f20df8c10397aac8d31bcb455913ad02bb67shubang                        } catch (RemoteException e) {
4078049f20df8c10397aac8d31bcb455913ad02bb67shubang                            Slog.e(TAG, "error in unregisterCallback", e);
4088049f20df8c10397aac8d31bcb455913ad02bb67shubang                        }
4098049f20df8c10397aac8d31bcb455913ad02bb67shubang                    }
4108049f20df8c10397aac8d31bcb455913ad02bb67shubang                    mContext.unbindService(serviceState.connection);
4118049f20df8c10397aac8d31bcb455913ad02bb67shubang                    it.remove();
4128049f20df8c10397aac8d31bcb455913ad02bb67shubang                }
4138049f20df8c10397aac8d31bcb455913ad02bb67shubang            }
4143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4158c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            mCurrentUserId = userId;
4164f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo            getOrCreateUserStateLocked(userId);
41719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            buildTvInputListLocked(userId, null);
4189c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            buildTvContentRatingSystemListLocked(userId);
4198c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_SWITCH_CONTENT_RESOLVER,
4208c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    getContentResolverForUser(userId)).sendToTarget();
4213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4248049f20df8c10397aac8d31bcb455913ad02bb67shubang    private void clearSessionAndNotifyClientLocked(SessionState state) {
4258049f20df8c10397aac8d31bcb455913ad02bb67shubang        if (state.client != null) {
4268049f20df8c10397aac8d31bcb455913ad02bb67shubang            try {
4278049f20df8c10397aac8d31bcb455913ad02bb67shubang                state.client.onSessionReleased(state.seq);
4288049f20df8c10397aac8d31bcb455913ad02bb67shubang            } catch(RemoteException e) {
4298049f20df8c10397aac8d31bcb455913ad02bb67shubang                Slog.e(TAG, "error in onSessionReleased", e);
4308049f20df8c10397aac8d31bcb455913ad02bb67shubang            }
4318049f20df8c10397aac8d31bcb455913ad02bb67shubang        }
4328049f20df8c10397aac8d31bcb455913ad02bb67shubang        // If there are any other sessions based on this session, they should be released.
4338049f20df8c10397aac8d31bcb455913ad02bb67shubang        UserState userState = getOrCreateUserStateLocked(state.userId);
4348049f20df8c10397aac8d31bcb455913ad02bb67shubang        for (SessionState sessionState : userState.sessionStateMap.values()) {
4358049f20df8c10397aac8d31bcb455913ad02bb67shubang            if (state.sessionToken == sessionState.hardwareSessionToken) {
4368049f20df8c10397aac8d31bcb455913ad02bb67shubang                releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID, state.userId);
4378049f20df8c10397aac8d31bcb455913ad02bb67shubang                try {
4388049f20df8c10397aac8d31bcb455913ad02bb67shubang                    sessionState.client.onSessionReleased(sessionState.seq);
4398049f20df8c10397aac8d31bcb455913ad02bb67shubang                } catch (RemoteException e) {
4408049f20df8c10397aac8d31bcb455913ad02bb67shubang                    Slog.e(TAG, "error in onSessionReleased", e);
4418049f20df8c10397aac8d31bcb455913ad02bb67shubang                }
4428049f20df8c10397aac8d31bcb455913ad02bb67shubang            }
4438049f20df8c10397aac8d31bcb455913ad02bb67shubang        }
4448049f20df8c10397aac8d31bcb455913ad02bb67shubang        removeSessionStateLocked(state.sessionToken, state.userId);
4458049f20df8c10397aac8d31bcb455913ad02bb67shubang    }
4468049f20df8c10397aac8d31bcb455913ad02bb67shubang
4473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void removeUser(int userId) {
4483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
449b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            UserState userState = mUserStates.get(userId);
450b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            if (userState == null) {
451b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo                return;
452b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            }
4538049f20df8c10397aac8d31bcb455913ad02bb67shubang            // Release all created sessions.
4548049f20df8c10397aac8d31bcb455913ad02bb67shubang            for (SessionState state : userState.sessionStateMap.values()) {
4558049f20df8c10397aac8d31bcb455913ad02bb67shubang                if (state.session != null) {
4568049f20df8c10397aac8d31bcb455913ad02bb67shubang                    try {
4578049f20df8c10397aac8d31bcb455913ad02bb67shubang                        state.session.release();
4588049f20df8c10397aac8d31bcb455913ad02bb67shubang                    } catch (RemoteException e) {
4598049f20df8c10397aac8d31bcb455913ad02bb67shubang                        Slog.e(TAG, "error in release", e);
4608049f20df8c10397aac8d31bcb455913ad02bb67shubang                    }
4618049f20df8c10397aac8d31bcb455913ad02bb67shubang                }
4628049f20df8c10397aac8d31bcb455913ad02bb67shubang            }
4638049f20df8c10397aac8d31bcb455913ad02bb67shubang            userState.sessionStateMap.clear();
4648049f20df8c10397aac8d31bcb455913ad02bb67shubang
4658049f20df8c10397aac8d31bcb455913ad02bb67shubang            // Unregister all callbacks and unbind all services.
4668049f20df8c10397aac8d31bcb455913ad02bb67shubang            for (ServiceState serviceState : userState.serviceStateMap.values()) {
4678049f20df8c10397aac8d31bcb455913ad02bb67shubang                if (serviceState.service != null) {
4688049f20df8c10397aac8d31bcb455913ad02bb67shubang                    if (serviceState.callback != null) {
4698049f20df8c10397aac8d31bcb455913ad02bb67shubang                        try {
4708049f20df8c10397aac8d31bcb455913ad02bb67shubang                            serviceState.service.unregisterCallback(serviceState.callback);
4718049f20df8c10397aac8d31bcb455913ad02bb67shubang                        } catch (RemoteException e) {
4728049f20df8c10397aac8d31bcb455913ad02bb67shubang                            Slog.e(TAG, "error in unregisterCallback", e);
4738049f20df8c10397aac8d31bcb455913ad02bb67shubang                        }
4748049f20df8c10397aac8d31bcb455913ad02bb67shubang                    }
4758049f20df8c10397aac8d31bcb455913ad02bb67shubang                    mContext.unbindService(serviceState.connection);
4768049f20df8c10397aac8d31bcb455913ad02bb67shubang                }
4778049f20df8c10397aac8d31bcb455913ad02bb67shubang            }
4788049f20df8c10397aac8d31bcb455913ad02bb67shubang            userState.serviceStateMap.clear();
4793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
480fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            // Clear everything else.
481fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            userState.inputMap.clear();
482fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            userState.packageSet.clear();
4839c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo            userState.contentRatingSystemList.clear();
48472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            userState.clientStateMap.clear();
485fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            userState.callbackSet.clear();
486fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            userState.mainSessionToken = null;
48772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
4883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.remove(userId);
4893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4928c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo    private ContentResolver getContentResolverForUser(int userId) {
4938c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        UserHandle user = new UserHandle(userId);
4948c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        Context context;
4958c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        try {
4968c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            context = mContext.createPackageContextAsUser("android", 0, user);
4978c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        } catch (NameNotFoundException e) {
4982a2b299dca20b151d5dc5bda3d068d70e6f15f6cJae Seo            Slog.e(TAG, "failed to create package context as user " + user);
4998c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo            context = mContext;
5008c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        }
5018c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        return context.getContentResolver();
5028c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo    }
5038c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo
5044f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo    private UserState getOrCreateUserStateLocked(int userId) {
5053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = mUserStates.get(userId);
5063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (userState == null) {
5074f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo            userState = new UserState(mContext, userId);
5084f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo            mUserStates.put(userId, userState);
5093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return userState;
5113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5139e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee    private ServiceState getServiceStateLocked(ComponentName component, int userId) {
5144f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
5159e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        ServiceState serviceState = userState.serviceStateMap.get(component);
5163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
5179e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            throw new IllegalStateException("Service state not found for " + component + " (userId="
5187de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    + userId + ")");
5193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return serviceState;
5213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5232b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
5244f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
5253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
5263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (sessionState == null) {
527fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            throw new SessionNotFoundException("Session state not found for token " + sessionToken);
5283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Only the application that requested this session or the system can access it.
530fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
5313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new SecurityException("Illegal access to the session with token " + sessionToken
5323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + " from uid " + callingUid);
5333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        return sessionState;
5352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
5362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
5384c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
5394c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee    }
5404c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee
5414c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee    private ITvInputSession getSessionLocked(SessionState sessionState) {
542fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        ITvInputSession session = sessionState.session;
5433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (session == null) {
5444c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            throw new IllegalStateException("Session not yet created for token "
545fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    + sessionState.sessionToken);
5463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return session;
5483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
5513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            String methodName) {
5523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
5533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                false, methodName, null);
5543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5569e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee    private void updateServiceConnectionLocked(ComponentName component, int userId) {
5574f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
5589e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        ServiceState serviceState = userState.serviceStateMap.get(component);
5593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
5603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return;
5613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
562fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        if (serviceState.reconnecting) {
563fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            if (!serviceState.sessionTokens.isEmpty()) {
5642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                // wait until all the sessions are removed.
5652b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                return;
5662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
567fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            serviceState.reconnecting = false;
5682b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
5698049f20df8c10397aac8d31bcb455913ad02bb67shubang
5708049f20df8c10397aac8d31bcb455913ad02bb67shubang        boolean shouldBind;
5718049f20df8c10397aac8d31bcb455913ad02bb67shubang        if (userId == mCurrentUserId) {
5728049f20df8c10397aac8d31bcb455913ad02bb67shubang            shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
5738049f20df8c10397aac8d31bcb455913ad02bb67shubang        } else {
5748049f20df8c10397aac8d31bcb455913ad02bb67shubang            // For a non-current user,
5758049f20df8c10397aac8d31bcb455913ad02bb67shubang            // if sessionTokens is not empty, it contains recording sessions only
5768049f20df8c10397aac8d31bcb455913ad02bb67shubang            // because other sessions must have been removed while switching user
5778049f20df8c10397aac8d31bcb455913ad02bb67shubang            // and non-recording sessions are not created by createSession().
5788049f20df8c10397aac8d31bcb455913ad02bb67shubang            shouldBind = !serviceState.sessionTokens.isEmpty();
5798049f20df8c10397aac8d31bcb455913ad02bb67shubang        }
5808049f20df8c10397aac8d31bcb455913ad02bb67shubang
5818049f20df8c10397aac8d31bcb455913ad02bb67shubang        if (serviceState.service == null && shouldBind) {
5823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is not yet connected but its state indicates that we
5833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // have pending requests. Then, connect the service.
584fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            if (serviceState.bound) {
5853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // We have already bound to the service so we don't try to bind again until after we
5863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // unbind later on.
5873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
5883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
5909e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
5913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
592d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim
5939e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
594fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            serviceState.bound = mContext.bindServiceAsUser(
595d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    i, serviceState.connection,
596d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
597d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    new UserHandle(userId));
5988049f20df8c10397aac8d31bcb455913ad02bb67shubang        } else if (serviceState.service != null && !shouldBind) {
5993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is already connected but its state indicates that we have
6003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // nothing to do with it. Then, disconnect the service.
6013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
6029e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                Slog.d(TAG, "unbindService(service=" + component + ")");
6033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
604fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            mContext.unbindService(serviceState.connection);
6059e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            userState.serviceStateMap.remove(component);
6063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
6083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
609426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang    private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
610426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang            String inputId, int userId) {
611426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang        // Let clients know the create session requests are failed.
6124f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
613f7f49ddade34744d5386f9bf52ab9ba4f981fce7Dongwon Kang        List<SessionState> sessionsToAbort = new ArrayList<>();
614fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        for (IBinder sessionToken : serviceState.sessionTokens) {
615426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang            SessionState sessionState = userState.sessionStateMap.get(sessionToken);
616fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            if (sessionState.session == null && (inputId == null
6172cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                    || sessionState.inputId.equals(inputId))) {
618f7f49ddade34744d5386f9bf52ab9ba4f981fce7Dongwon Kang                sessionsToAbort.add(sessionState);
619426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang            }
620426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang        }
621f7f49ddade34744d5386f9bf52ab9ba4f981fce7Dongwon Kang        for (SessionState sessionState : sessionsToAbort) {
622fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
623fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            sendSessionTokenToClientLocked(sessionState.client,
6242cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                    sessionState.inputId, null, null, sessionState.seq);
625f7f49ddade34744d5386f9bf52ab9ba4f981fce7Dongwon Kang        }
626fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        updateServiceConnectionLocked(serviceState.component, userId);
627426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang    }
628426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang
629fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
630fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            int userId) {
6314f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
632fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
6333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (DEBUG) {
6342cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")");
6353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
636fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
6376a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
6383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Set up a callback to send the session token.
639fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
6403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Create a session. When failed, send a null token immediately.
6423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
643a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            if (sessionState.isRecordingSession) {
6442cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                service.createRecordingSession(callback, sessionState.inputId);
645a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            } else {
6462cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                service.createSession(channels[1], callback, sessionState.inputId);
647a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
6483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException e) {
6499a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in createSession", e);
650fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang            removeSessionStateLocked(sessionToken, userId);
6512cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo            sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
652fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    null, sessionState.seq);
6533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6546a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        channels[1].dispose();
6553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
6563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
657d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
6585c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            IBinder sessionToken, InputChannel channel, int seq) {
6593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
660d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            client.onSessionCreated(inputId, sessionToken, channel, seq);
661fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo        } catch (RemoteException e) {
662fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            Slog.e(TAG, "error in onSessionCreated", e);
6633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
6653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
667fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        SessionState sessionState = null;
668fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        try {
669fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
670fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            if (sessionState.session != null) {
6714f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                UserState userState = getOrCreateUserStateLocked(userId);
672fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                if (sessionToken == userState.mainSessionToken) {
673fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    setMainLocked(sessionToken, false, callingUid, userId);
674fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                }
675e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo                sessionState.session.release();
6762b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
677fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        } catch (RemoteException | SessionNotFoundException e) {
678fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            Slog.e(TAG, "error in releaseSession", e);
679fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        } finally {
680fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            if (sessionState != null) {
681fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                sessionState.session = null;
682fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            }
6833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        removeSessionStateLocked(sessionToken, userId);
6853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
6863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
687fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
6884f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
689abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee        if (sessionToken == userState.mainSessionToken) {
69015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            if (DEBUG) {
69115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                Slog.d(TAG, "mainSessionToken=null");
69215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            }
693abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee            userState.mainSessionToken = null;
694abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee        }
695abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee
696abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee        // Remove the session state from the global session state map of the current user.
697fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
698fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
6998d4ded0058de5c573ccf79c4596bf5eb1b14fad3Chulwoo Lee        if (sessionState == null) {
7008d4ded0058de5c573ccf79c4596bf5eb1b14fad3Chulwoo Lee            return;
7018d4ded0058de5c573ccf79c4596bf5eb1b14fad3Chulwoo Lee        }
7028d4ded0058de5c573ccf79c4596bf5eb1b14fad3Chulwoo Lee
70372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // Also remove the session token from the session token list of the current client and
70472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // service.
705fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
70672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (clientState != null) {
707fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            clientState.sessionTokens.remove(sessionToken);
70872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            if (clientState.isEmpty()) {
709fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                userState.clientStateMap.remove(sessionState.client.asBinder());
71072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
71172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
71272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
7132cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo        ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName);
7142cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo        if (serviceState != null) {
7152cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo            serviceState.sessionTokens.remove(sessionToken);
716fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        }
7172cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo        updateServiceConnectionLocked(sessionState.componentName, userId);
7187eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
7197eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // Log the end of watch.
7207eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        SomeArgs args = SomeArgs.obtain();
7217eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        args.arg1 = sessionToken;
7227eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        args.arg2 = System.currentTimeMillis();
7237eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget();
724fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    }
725fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
72615c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee    private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
72715c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee        try {
728fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
729fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            if (sessionState.hardwareSessionToken != null) {
730fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
731fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                        Process.SYSTEM_UID, userId);
732fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            }
7332cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo            ServiceState serviceState = getServiceStateLocked(sessionState.componentName, userId);
734fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            if (!serviceState.isHardware) {
735fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                return;
736fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            }
737fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            ITvInputSession session = getSessionLocked(sessionState);
73815c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            session.setMain(isMain);
739fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        } catch (RemoteException | SessionNotFoundException e) {
74015c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            Slog.e(TAG, "error in setMain", e);
74115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee        }
74215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee    }
74315c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee
7448e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    private void notifyInputAddedLocked(UserState userState, String inputId) {
7458e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        if (DEBUG) {
746fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")");
7478e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
7488e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        for (ITvInputManagerCallback callback : userState.callbackSet) {
7498e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            try {
7508e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                callback.onInputAdded(inputId);
7518e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            } catch (RemoteException e) {
752fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.e(TAG, "failed to report added input to callback", e);
7538e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            }
7548e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
7558e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    }
7568e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
7578e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    private void notifyInputRemovedLocked(UserState userState, String inputId) {
7588e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        if (DEBUG) {
759fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")");
7608e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
7618e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        for (ITvInputManagerCallback callback : userState.callbackSet) {
7628e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            try {
7638e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                callback.onInputRemoved(inputId);
7648e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            } catch (RemoteException e) {
765fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.e(TAG, "failed to report removed input to callback", e);
7668e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            }
7678e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim        }
7688e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    }
7698e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim
77019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee    private void notifyInputUpdatedLocked(UserState userState, String inputId) {
77119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        if (DEBUG) {
77219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")");
77319ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        }
77419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        for (ITvInputManagerCallback callback : userState.callbackSet) {
77519ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            try {
77619ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                callback.onInputUpdated(inputId);
77719ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            } catch (RemoteException e) {
77819ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                Slog.e(TAG, "failed to report updated input to callback", e);
77919ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            }
78019ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee        }
78119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee    }
78219ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee
7838e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim    private void notifyInputStateChangedLocked(UserState userState, String inputId,
784969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            int state, ITvInputManagerCallback targetCallback) {
785969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (DEBUG) {
786fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo            Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId
787fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    + ", state=" + state + ")");
788969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
789969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (targetCallback == null) {
790969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (ITvInputManagerCallback callback : userState.callbackSet) {
791969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                try {
792969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    callback.onInputStateChanged(inputId, state);
793969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                } catch (RemoteException e) {
794fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    Slog.e(TAG, "failed to report state change to callback", e);
795969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
796969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
797969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        } else {
7982b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
799969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                targetCallback.onInputStateChanged(inputId, state);
8002b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
801fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.e(TAG, "failed to report state change to callback", e);
8022b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
8032b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
8042b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
8052b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
806aa5605ffee270ef8802c5d9dc8df8ce71e377f55Jae Seo    private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) {
807a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        if (DEBUG) {
808aa5605ffee270ef8802c5d9dc8df8ce71e377f55Jae Seo            Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")");
809a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
810abda420b9d2959776093230cd2e157a6080f2f64Jae Seo        String inputId = inputInfo.getId();
811abda420b9d2959776093230cd2e157a6080f2f64Jae Seo        TvInputState inputState = userState.inputMap.get(inputId);
812abda420b9d2959776093230cd2e157a6080f2f64Jae Seo        if (inputState == null) {
813abda420b9d2959776093230cd2e157a6080f2f64Jae Seo            Slog.e(TAG, "failed to set input info - unknown input id " + inputId);
814abda420b9d2959776093230cd2e157a6080f2f64Jae Seo            return;
815abda420b9d2959776093230cd2e157a6080f2f64Jae Seo        }
816abda420b9d2959776093230cd2e157a6080f2f64Jae Seo        inputState.info = inputInfo;
817abda420b9d2959776093230cd2e157a6080f2f64Jae Seo
818a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        for (ITvInputManagerCallback callback : userState.callbackSet) {
819a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            try {
820aa5605ffee270ef8802c5d9dc8df8ce71e377f55Jae Seo                callback.onTvInputInfoUpdated(inputInfo);
821a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            } catch (RemoteException e) {
822aa5605ffee270ef8802c5d9dc8df8ce71e377f55Jae Seo                Slog.e(TAG, "failed to report updated input info to callback", e);
823a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
824a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
825a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo    }
826a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
827969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private void setStateLocked(String inputId, int state, int userId) {
8284f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo        UserState userState = getOrCreateUserStateLocked(userId);
829969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        TvInputState inputState = userState.inputMap.get(inputId);
830fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
831fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        int oldState = inputState.state;
832fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        inputState.state = state;
833fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        if (serviceState != null && serviceState.service == null
8348049f20df8c10397aac8d31bcb455913ad02bb67shubang                && (!serviceState.sessionTokens.isEmpty() || serviceState.isHardware)) {
835969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            // We don't notify state change while reconnecting. It should remain disconnected.
836969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return;
837969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
838969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (oldState != state) {
8398e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim            notifyInputStateChangedLocked(userState, inputId, state, null);
840969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
841969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
842969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
8433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class BinderService extends ITvInputManager.Stub {
8443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
8453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public List<TvInputInfo> getTvInputList(int userId) {
8463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
8473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getTvInputList");
8483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
8493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
8503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
8514f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
8526e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    List<TvInputInfo> inputList = new ArrayList<>();
853969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (TvInputState state : userState.inputMap.values()) {
854fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        inputList.add(state.info);
8553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
856969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    return inputList;
8573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
8593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
8603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
861b375805f3b1672e68d1511565af4700e5fa8491dJae Seo        }
862b375805f3b1672e68d1511565af4700e5fa8491dJae Seo
863b375805f3b1672e68d1511565af4700e5fa8491dJae Seo        @Override
864b375805f3b1672e68d1511565af4700e5fa8491dJae Seo        public TvInputInfo getTvInputInfo(String inputId, int userId) {
865b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
866b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                    Binder.getCallingUid(), userId, "getTvInputInfo");
867b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            final long identity = Binder.clearCallingIdentity();
868b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            try {
869b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                synchronized (mLock) {
8704f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
871b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                    TvInputState state = userState.inputMap.get(inputId);
872fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return state == null ? null : state.info;
873b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                }
874b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            } finally {
875b375805f3b1672e68d1511565af4700e5fa8491dJae Seo                Binder.restoreCallingIdentity(identity);
876b375805f3b1672e68d1511565af4700e5fa8491dJae Seo            }
8773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
879aa5605ffee270ef8802c5d9dc8df8ce71e377f55Jae Seo        public void updateTvInputInfo(TvInputInfo inputInfo, int userId) {
880c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            String inputInfoPackageName = inputInfo.getServiceInfo().packageName;
881c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            String callingPackageName = getCallingPackageName();
8829d0e0f14edf9204d655df2b4cf4d962b4254c541Dongwon Kang            if (!TextUtils.equals(inputInfoPackageName, callingPackageName)
8839d0e0f14edf9204d655df2b4cf4d962b4254c541Dongwon Kang                    && mContext.checkCallingPermission(
8849d0e0f14edf9204d655df2b4cf4d962b4254c541Dongwon Kang                            android.Manifest.permission.WRITE_SECURE_SETTINGS)
8859d0e0f14edf9204d655df2b4cf4d962b4254c541Dongwon Kang                                    != PackageManager.PERMISSION_GRANTED) {
8869d0e0f14edf9204d655df2b4cf4d962b4254c541Dongwon Kang                // Only the app owning the input and system settings are allowed to update info.
887c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo                throw new IllegalArgumentException("calling package " + callingPackageName
888c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo                        + " is not allowed to change TvInputInfo for " + inputInfoPackageName);
889c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            }
890c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo
891c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
892aa5605ffee270ef8802c5d9dc8df8ce71e377f55Jae Seo                    Binder.getCallingUid(), userId, "updateTvInputInfo");
893c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            final long identity = Binder.clearCallingIdentity();
894c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            try {
895c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo                synchronized (mLock) {
896c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
897aa5605ffee270ef8802c5d9dc8df8ce71e377f55Jae Seo                    updateTvInputInfoLocked(userState, inputInfo);
898c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo                }
899c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            } finally {
900c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo                Binder.restoreCallingIdentity(identity);
901c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            }
902c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo        }
903c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo
904c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo        private String getCallingPackageName() {
905c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            final String[] packages = mContext.getPackageManager().getPackagesForUid(
906c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo                    Binder.getCallingUid());
907c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            if (packages != null && packages.length > 0) {
908c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo                return packages[0];
909c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            }
910c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo            return "unknown";
911c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo        }
912c2a89510ddda390d6d53ff24dd20d257fcd2379eJae Seo
9133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
914993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang        public int getTvInputState(String inputId, int userId) {
915993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
916993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                    Binder.getCallingUid(), userId, "getTvInputState");
917993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            final long identity = Binder.clearCallingIdentity();
918993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            try {
919993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                synchronized (mLock) {
9204f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
921993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                    TvInputState state = userState.inputMap.get(inputId);
92282fce64530d19a4da1c02d424fb2515feafe6a70Jae Seo                    return state == null ? INPUT_STATE_CONNECTED : state.state;
923993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                }
924993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            } finally {
925993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang                Binder.restoreCallingIdentity(identity);
926993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang            }
927993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang        }
928993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang
929993f81e2380da210c27e1e957ac1bdca3a99100aDongwon Kang        @Override
9309c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) {
9315c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
9329c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                    Binder.getCallingUid(), userId, "getTvContentRatingSystemList");
9335c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            final long identity = Binder.clearCallingIdentity();
9345c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            try {
9355c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim                synchronized (mLock) {
9364f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
9379c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo                    return userState.contentRatingSystemList;
9385c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim                }
9395c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            } finally {
9405c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim                Binder.restoreCallingIdentity(identity);
9415c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim            }
9425c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim        }
9435c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim
9445c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim        @Override
945558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen        public void sendTvInputNotifyIntent(Intent intent, int userId) {
946558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
947558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    != PackageManager.PERMISSION_GRANTED) {
948558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                throw new SecurityException("The caller: " + getCallingPackageName()
949558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                        + " doesn't have permission: "
950558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                        + android.Manifest.permission.NOTIFY_TV_INPUTS);
951558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            }
952558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            if (TextUtils.isEmpty(intent.getPackage())) {
953558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                throw new IllegalArgumentException("Must specify package name to notify.");
954558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            }
955558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            switch (intent.getAction()) {
956558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                case TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
957558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
958558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                        throw new IllegalArgumentException("Invalid preview program ID.");
959558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    }
960558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    break;
961558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                case TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
962558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
963558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                        throw new IllegalArgumentException("Invalid watch next program ID.");
964558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    }
965558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    break;
966558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                case TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
967558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
968558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                        throw new IllegalArgumentException("Invalid preview program ID.");
969558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    }
970558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
971558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                        throw new IllegalArgumentException("Invalid watch next program ID.");
972558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    }
973558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    break;
974558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                default:
975558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    throw new IllegalArgumentException("Invalid TV input notifying action: "
976558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                            + intent.getAction());
977558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            }
978558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
979558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                    Binder.getCallingUid(), userId, "sendTvInputNotifyIntent");
980558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            final long identity = Binder.clearCallingIdentity();
981558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            try {
982558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
983558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            } finally {
984558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen                Binder.restoreCallingIdentity(identity);
985558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen            }
986558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen        }
987558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen
988558acf96dbffc0f13b414b1a5c5de191f6ffe27aConrad Chen        @Override
989969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void registerCallback(final ITvInputManagerCallback callback, int userId) {
9903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
9913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "registerCallback");
9923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
9933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
9943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
9954f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
996969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    userState.callbackSet.add(callback);
997fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    try {
998fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        callback.asBinder().linkToDeath(new IBinder.DeathRecipient() {
999fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                            @Override
1000fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                            public void binderDied() {
1001fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                synchronized (mLock) {
1002fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                    if (userState.callbackSet != null) {
1003fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                        userState.callbackSet.remove(callback);
1004fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                    }
1005fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                                }
1006fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                            }
1007fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        }, 0);
1008fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    } catch (RemoteException e) {
1009fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        Slog.e(TAG, "client process has already died", e);
1010fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    }
10113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
10123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
10133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
10143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
1018969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
10193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
10203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "unregisterCallback");
10213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
10223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
10233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
10244f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1025969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    userState.callbackSet.remove(callback);
10263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
10273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
10283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
10293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
1033783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public boolean isParentalControlsEnabled(int userId) {
1034783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1035783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "isParentalControlsEnabled");
1036783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
1037783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
1038783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
10394f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1040783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    return userState.persistentDataStore.isParentalControlsEnabled();
1041783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
1042783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
1043783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
1044783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
1045783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
1046783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1047783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
1048783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public void setParentalControlsEnabled(boolean enabled, int userId) {
1049783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            ensureParentalControlsPermission();
1050783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1051783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "setParentalControlsEnabled");
1052783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
1053783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
1054783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
10554f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1056783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    userState.persistentDataStore.setParentalControlsEnabled(enabled);
1057783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
1058783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
1059783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
1060783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
1061783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
1062783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1063783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
1064783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public boolean isRatingBlocked(String rating, int userId) {
1065783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1066783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "isRatingBlocked");
1067783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
1068783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
1069783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
10704f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1071783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    return userState.persistentDataStore.isRatingBlocked(
1072783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            TvContentRating.unflattenFromString(rating));
1073783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
1074783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
1075783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
1076783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
1077783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
1078783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1079783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
1080783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public List<String> getBlockedRatings(int userId) {
1081783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1082783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "getBlockedRatings");
1083783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
1084783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
1085783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
10864f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
10876e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    List<String> ratings = new ArrayList<>();
1088783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    for (TvContentRating rating
1089783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            : userState.persistentDataStore.getBlockedRatings()) {
1090783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                        ratings.add(rating.flattenToString());
1091783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    }
1092783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    return ratings;
1093783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
1094783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
1095783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
1096783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
1097783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
1098783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1099783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
1100783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public void addBlockedRating(String rating, int userId) {
1101783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            ensureParentalControlsPermission();
1102783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1103783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "addBlockedRating");
1104783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
1105783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
1106783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
11074f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1108783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    userState.persistentDataStore.addBlockedRating(
1109783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            TvContentRating.unflattenFromString(rating));
1110783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
1111783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
1112783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
1113783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
1114783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
1115783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1116783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
1117783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public void removeBlockedRating(String rating, int userId) {
1118783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            ensureParentalControlsPermission();
1119783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1120783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    Binder.getCallingUid(), userId, "removeBlockedRating");
1121783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            final long identity = Binder.clearCallingIdentity();
1122783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
1123783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                synchronized (mLock) {
11244f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1125783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    userState.persistentDataStore.removeBlockedRating(
1126783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            TvContentRating.unflattenFromString(rating));
1127783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
1128783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
1129783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                Binder.restoreCallingIdentity(identity);
1130783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
1131783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
1132783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1133783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        private void ensureParentalControlsPermission() {
1134fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo            if (mContext.checkCallingPermission(
1135fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo                    android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
1136fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo                    != PackageManager.PERMISSION_GRANTED) {
1137fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo                throw new SecurityException(
1138fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo                        "The caller does not have parental controls permission");
1139fc836f6684f6e142fe53dc16e1552ffd19bd95bcJae Seo            }
1140783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
1141783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
1142783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
1143d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void createSession(final ITvInputClient client, final String inputId,
1144a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                boolean isRecordingSession, int seq, int userId) {
11453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
11463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
11473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "createSession");
11483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
11493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
11503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
11518049f20df8c10397aac8d31bcb455913ad02bb67shubang                    if (userId != mCurrentUserId && !isRecordingSession) {
11528049f20df8c10397aac8d31bcb455913ad02bb67shubang                        // A non-recording session of a backgroud (non-current) user
11538049f20df8c10397aac8d31bcb455913ad02bb67shubang                        // should not be created.
11548049f20df8c10397aac8d31bcb455913ad02bb67shubang                        // Let the client get onConnectionFailed callback for this case.
11558049f20df8c10397aac8d31bcb455913ad02bb67shubang                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
11568049f20df8c10397aac8d31bcb455913ad02bb67shubang                        return;
11578049f20df8c10397aac8d31bcb455913ad02bb67shubang                    }
11584f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1159426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    TvInputState inputState = userState.inputMap.get(inputId);
1160426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    if (inputState == null) {
1161426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                        Slog.w(TAG, "Failed to find input state for inputId=" + inputId);
1162426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1163426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                        return;
1164426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    }
1165fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    TvInputInfo info = inputState.info;
1166187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
11673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
1168187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        serviceState = new ServiceState(info.getComponent(), resolvedUserId);
1169187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        userState.serviceStateMap.put(info.getComponent(), serviceState);
11703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
11712b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send a null token immediately while reconnecting.
11726e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    if (serviceState.reconnecting) {
11735c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
11742b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        return;
11752b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
11762b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
11772b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Create a new session token and a session state.
11782b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    IBinder sessionToken = new Binder();
11792cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                    SessionState sessionState = new SessionState(sessionToken, info.getId(),
11802cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                            info.getComponent(), isRecordingSession, client, seq, callingUid,
11812cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                            resolvedUserId);
11822b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
11832b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Add them to the global session state map of the current user.
11842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    userState.sessionStateMap.put(sessionToken, sessionState);
11852b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
11862b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Also, add them to the session state map of the current service.
1187fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.sessionTokens.add(sessionToken);
11883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1189fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (serviceState.service != null) {
1190fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        createSessionInternalLocked(serviceState.service, sessionToken,
11917de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                                resolvedUserId);
11923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
1193187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
11943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
11953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
11963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
11973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
11983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
11993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
12003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
12013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
12023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void releaseSession(IBinder sessionToken, int userId) {
120315c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            if (DEBUG) {
1204fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
120515c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            }
12063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
12073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
12083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "releaseSession");
12093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
12103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
12113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
12122b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
12133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
12143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
12153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
12163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
12173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
12183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
12193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
12204c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        public void setMainSession(IBinder sessionToken, int userId) {
122115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            if (DEBUG) {
1222fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")");
122315c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee            }
12244c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            final int callingUid = Binder.getCallingUid();
12254c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
12264c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    userId, "setMainSession");
12274c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            final long identity = Binder.clearCallingIdentity();
12284c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            try {
12294c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                synchronized (mLock) {
12304f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1231956afc2ba79f50bb8025c6d334653e3c3419b480Ji-Hwan Lee                    if (userState.mainSessionToken == sessionToken) {
12324c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                        return;
12334c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    }
123415c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    if (DEBUG) {
123515c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                        Slog.d(TAG, "mainSessionToken=" + sessionToken);
1236abca0ee7949f59e72b8d2764dafa23af18eb51dbJi-Hwan Lee                    }
123715c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    IBinder oldMainSessionToken = userState.mainSessionToken;
12384c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    userState.mainSessionToken = sessionToken;
12394c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee
1240956afc2ba79f50bb8025c6d334653e3c3419b480Ji-Hwan Lee                    // Inform the new main session first.
124115c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    // See {@link TvInputService.Session#onSetMain}.
124215c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    if (sessionToken != null) {
124315c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                        setMainLocked(sessionToken, true, callingUid, userId);
12444c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    }
124515c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                    if (oldMainSessionToken != null) {
124615c56aac985bc8d75f38fb4ecb92dda12d2ca06cJi-Hwan Lee                        setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId);
12474c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                    }
12484c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                }
12494c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            } finally {
12504c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee                Binder.restoreCallingIdentity(identity);
12514c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee            }
12524c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        }
12534c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee
12544c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        @Override
12553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
12563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
12573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
12583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setSurface");
12593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
12603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
12613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
12623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1263bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1264bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                resolvedUserId);
1265fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionState.hardwareSessionToken == null) {
1266bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            getSessionLocked(sessionState).setSurface(surface);
1267bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        } else {
1268fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            getSessionLocked(sessionState.hardwareSessionToken,
1269bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                    Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
1270bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1271fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
12729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setSurface", e);
12733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
12743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
12753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
1276f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                if (surface != null) {
1277f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    // surface is not used in TvInputManagerService.
1278f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    surface.release();
1279f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                }
12803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
12813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
12823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
12833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
12843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
1285e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
1286e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                int height, int userId) {
1287e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final int callingUid = Binder.getCallingUid();
1288e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1289e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    userId, "dispatchSurfaceChanged");
1290e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final long identity = Binder.clearCallingIdentity();
1291e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            try {
1292e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                synchronized (mLock) {
1293e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    try {
1294bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1295bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                resolvedUserId);
1296fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
1297fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                height);
1298fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionState.hardwareSessionToken != null) {
1299fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
1300bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                    resolvedUserId).dispatchSurfaceChanged(format, width, height);
1301bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1302fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
1303e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                        Slog.e(TAG, "error in dispatchSurfaceChanged", e);
1304e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    }
1305e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                }
1306e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            } finally {
1307e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                Binder.restoreCallingIdentity(identity);
1308e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            }
1309e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        }
1310e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho
1311e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        @Override
13123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setVolume(IBinder sessionToken, float volume, int userId) {
1313bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            final float REMOTE_VOLUME_ON = 1.0f;
1314bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang            final float REMOTE_VOLUME_OFF = 0f;
13153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
13163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
13173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setVolume");
13183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
13193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
13203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
13213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1322bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1323bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                resolvedUserId);
1324bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        getSessionLocked(sessionState).setVolume(volume);
1325fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionState.hardwareSessionToken != null) {
1326bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            // Here, we let the hardware session know only whether volume is on or
1327bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            // off to prevent that the volume is controlled in the both side.
1328fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            getSessionLocked(sessionState.hardwareSessionToken,
1329bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                    Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
1330bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                                            ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
1331bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1332fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
13339a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setVolume", e);
13343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
13353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
13363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
13373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
13383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
13393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
13403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
13413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
13421a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim        public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
13433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
13443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
13453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "tune");
13463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
13473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
13483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
13493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
13501a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
13511a6b25eabcc1fb66e6e8d76f91fd413e18b793a9Sungsoo Lim                                channelUri, params);
1352c22d0c0941ab65ca69977d002c4431394a735c7dJae Seo                        if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
1353008f6d4e326f6372e165bdf342178ecd1e834e2fYoungsang Cho                            // Do not log the watch history for passthrough inputs.
1354008f6d4e326f6372e165bdf342178ecd1e834e2fYoungsang Cho                            return;
1355008f6d4e326f6372e165bdf342178ecd1e834e2fYoungsang Cho                        }
135631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
13574f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                        UserState userState = getOrCreateUserStateLocked(resolvedUserId);
135831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
1359e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo                        if (sessionState.isRecordingSession) {
1360e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo                            return;
1361e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo                        }
136231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
13637eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        // Log the start of watch.
136431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SomeArgs args = SomeArgs.obtain();
13652cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                        args.arg1 = sessionState.componentName.getPackageName();
13667eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        args.arg2 = System.currentTimeMillis();
13677eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        args.arg3 = ContentUris.parseId(channelUri);
13687eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        args.arg4 = params;
13697eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        args.arg5 = sessionToken;
13707eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args)
13717eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                                .sendToTarget();
1372fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
13739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in tune", e);
13743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
13753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
13763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
13773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
13783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
13793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
13809a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
13819a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
1382a90338396c90f19b062b696cdb1ffcb8600755b2Jae Seo        public void unblockContent(
13839bf671f8ee72b156f16fcf05a3d1c6e093ecba67Sungsoo Lim                IBinder sessionToken, String unblockedRating, int userId) {
1384e12d810e99da093d3cf38f89c81e3e8d1e75b404Dongwon Kang            ensureParentalControlsPermission();
1385903d6b72cd572665309633e925485464d08bb25aJaewan Kim            final int callingUid = Binder.getCallingUid();
1386903d6b72cd572665309633e925485464d08bb25aJaewan Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1387903d6b72cd572665309633e925485464d08bb25aJaewan Kim                    userId, "unblockContent");
1388903d6b72cd572665309633e925485464d08bb25aJaewan Kim            final long identity = Binder.clearCallingIdentity();
1389903d6b72cd572665309633e925485464d08bb25aJaewan Kim            try {
1390903d6b72cd572665309633e925485464d08bb25aJaewan Kim                synchronized (mLock) {
1391903d6b72cd572665309633e925485464d08bb25aJaewan Kim                    try {
1392903d6b72cd572665309633e925485464d08bb25aJaewan Kim                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1393a90338396c90f19b062b696cdb1ffcb8600755b2Jae Seo                                .unblockContent(unblockedRating);
1394fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
1395a90338396c90f19b062b696cdb1ffcb8600755b2Jae Seo                        Slog.e(TAG, "error in unblockContent", e);
1396903d6b72cd572665309633e925485464d08bb25aJaewan Kim                    }
1397903d6b72cd572665309633e925485464d08bb25aJaewan Kim                }
1398903d6b72cd572665309633e925485464d08bb25aJaewan Kim            } finally {
1399903d6b72cd572665309633e925485464d08bb25aJaewan Kim                Binder.restoreCallingIdentity(identity);
1400903d6b72cd572665309633e925485464d08bb25aJaewan Kim            }
1401903d6b72cd572665309633e925485464d08bb25aJaewan Kim        }
1402903d6b72cd572665309633e925485464d08bb25aJaewan Kim
1403903d6b72cd572665309633e925485464d08bb25aJaewan Kim        @Override
14042c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
14052c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final int callingUid = Binder.getCallingUid();
14062c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
14072c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    userId, "setCaptionEnabled");
14082c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final long identity = Binder.clearCallingIdentity();
14092c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            try {
14102c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                synchronized (mLock) {
14112c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    try {
14122c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
14132c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                                .setCaptionEnabled(enabled);
1414fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
14152c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                        Slog.e(TAG, "error in setCaptionEnabled", e);
14162c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    }
14172c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                }
14182c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            } finally {
14192c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                Binder.restoreCallingIdentity(identity);
14202c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            }
14212c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        }
14222c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo
14232c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        @Override
142410d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo        public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
14251f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final int callingUid = Binder.getCallingUid();
14261f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
14271f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    userId, "selectTrack");
14281f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final long identity = Binder.clearCallingIdentity();
14291f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            try {
14301f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                synchronized (mLock) {
14311f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    try {
14321f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
143310d285ac06b3d3060c7d90d3dc196d4ac8367467Jae Seo                                type, trackId);
1434fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
14351f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        Slog.e(TAG, "error in selectTrack", e);
14361f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    }
14371f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                }
14381f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            } finally {
14391f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                Binder.restoreCallingIdentity(identity);
14401f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            }
14411f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        }
14421f213914c45c23c653f721690da2ce0718e63139Dongwon Kang
14431f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        @Override
1444a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
1445a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                int userId) {
1446a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            final int callingUid = Binder.getCallingUid();
1447a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1448a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                    userId, "sendAppPrivateCommand");
1449a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            final long identity = Binder.clearCallingIdentity();
1450a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            try {
1451a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                synchronized (mLock) {
1452a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                    try {
1453a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1454a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                                .appPrivateCommand(command, data);
1455fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
1456fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        Slog.e(TAG, "error in appPrivateCommand", e);
1457a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                    }
1458a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                }
1459a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            } finally {
1460a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo                Binder.restoreCallingIdentity(identity);
1461a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo            }
1462a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        }
1463a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo
1464a759b111a1c9cb00284038f8a1554bf29709b952Jae Seo        @Override
14659a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
14669a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                int userId) {
14679a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
14689a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
14699a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "createOverlayView");
14709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
14719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
14729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
14739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
14749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
14759a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .createOverlayView(windowToken, frame);
1476fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
14779a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in createOverlayView", e);
14789a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
14799a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
14809a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
14819a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
14829a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
14839a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
14849a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
14859a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
14869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
14879a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
14889a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
14899a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "relayoutOverlayView");
14909a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
14919a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
14929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
14939a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
14949a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
14959a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .relayoutOverlayView(frame);
1496fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
14979a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in relayoutOverlayView", e);
14989a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
14999a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
15009a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
15019a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
15029a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
15039a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
15049a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
15059a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
15069a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void removeOverlayView(IBinder sessionToken, int userId) {
15079a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
15089a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
15099a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "removeOverlayView");
15109a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
15119a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
15129a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
15139a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
15149a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
15159a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .removeOverlayView();
1516fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
15179a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in removeOverlayView", e);
15189a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
15199a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
15209a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
15219a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
15229a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
15239a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
1524c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1525c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1526a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
1527a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final int callingUid = Binder.getCallingUid();
1528a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1529a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    userId, "timeShiftPlay");
1530a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final long identity = Binder.clearCallingIdentity();
1531a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            try {
1532a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                synchronized (mLock) {
1533a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    try {
1534a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay(
1535a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                                recordedProgramUri);
1536a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } catch (RemoteException | SessionNotFoundException e) {
1537a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        Slog.e(TAG, "error in timeShiftPlay", e);
1538a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
1539a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1540a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            } finally {
1541a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                Binder.restoreCallingIdentity(identity);
1542a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
1543a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1544a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1545a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        @Override
15466f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void timeShiftPause(IBinder sessionToken, int userId) {
15476f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
15486f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
15496f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    userId, "timeShiftPause");
15506f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
15516f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
15526f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
15536f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
1554a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause();
15556f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
15566f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        Slog.e(TAG, "error in timeShiftPause", e);
15576f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
15586f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
15596f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
15606f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
15616f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
15626f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
15636f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
15646f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
15656f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void timeShiftResume(IBinder sessionToken, int userId) {
15666f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
15676f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
15686f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    userId, "timeShiftResume");
15696f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
15706f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
15716f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
15726f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
15736f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
15746f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                                .timeShiftResume();
15756f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
15766f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        Slog.e(TAG, "error in timeShiftResume", e);
15776f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
15786f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
15796f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
15806f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
15816f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
15826f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
15836f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
15846f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
15856f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId) {
15866f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
15876f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
15886f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    userId, "timeShiftSeekTo");
15896f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
15906f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
15916f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
15926f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
15936f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
15946f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                                .timeShiftSeekTo(timeMs);
15956f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
15966f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        Slog.e(TAG, "error in timeShiftSeekTo", e);
15976f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
15986f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
15996f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
16006f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
16016f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
16026f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
16036f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
16046f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
16054b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seo        public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params,
16066f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                int userId) {
16076f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
16086f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
16094b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seo                    userId, "timeShiftSetPlaybackParams");
16106f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
16116f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
16126f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
16136f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
16146f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
16154b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seo                                .timeShiftSetPlaybackParams(params);
16166f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
16174b34cc77630112d00e9a87498d05f5f8803a9ff6Jae Seo                        Slog.e(TAG, "error in timeShiftSetPlaybackParams", e);
16186f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
16196f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
16206f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
16216f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
16226f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
16236f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
16246f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
16256f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
1626465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo        public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable,
16276f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                int userId) {
16286f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int callingUid = Binder.getCallingUid();
16296f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1630465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                    userId, "timeShiftEnablePositionTracking");
16316f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            final long identity = Binder.clearCallingIdentity();
16326f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            try {
16336f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                synchronized (mLock) {
16346f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    try {
16356f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1636465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                                .timeShiftEnablePositionTracking(enable);
16376f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    } catch (RemoteException | SessionNotFoundException e) {
1638465f0d6aa36f2f1db88603aa487bcba9f5af068dJae Seo                        Slog.e(TAG, "error in timeShiftEnablePositionTracking", e);
16396f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    }
16406f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
16416f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            } finally {
16426f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                Binder.restoreCallingIdentity(identity);
16436f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
16446f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
16456f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
16466f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
16470cb5244e52590214ddc16dd5fc1030b5baf04726Dongwon Kang        public void startRecording(IBinder sessionToken, @Nullable Uri programUri, int userId) {
1648a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final int callingUid = Binder.getCallingUid();
1649a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1650a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    userId, "startRecording");
1651a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final long identity = Binder.clearCallingIdentity();
1652a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            try {
1653a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                synchronized (mLock) {
1654a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    try {
16554eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording(
16560cb5244e52590214ddc16dd5fc1030b5baf04726Dongwon Kang                                programUri);
1657a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } catch (RemoteException | SessionNotFoundException e) {
1658a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        Slog.e(TAG, "error in startRecording", e);
1659a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
1660a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1661a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            } finally {
1662a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                Binder.restoreCallingIdentity(identity);
1663a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
1664a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1665a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1666a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        @Override
1667a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public void stopRecording(IBinder sessionToken, int userId) {
1668a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final int callingUid = Binder.getCallingUid();
1669a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1670a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    userId, "stopRecording");
1671a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            final long identity = Binder.clearCallingIdentity();
1672a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            try {
1673a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                synchronized (mLock) {
1674a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    try {
1675a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording();
1676a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    } catch (RemoteException | SessionNotFoundException e) {
1677a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                        Slog.e(TAG, "error in stopRecording", e);
1678a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    }
1679a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
1680a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            } finally {
1681a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                Binder.restoreCallingIdentity(identity);
1682a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
1683a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
1684a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
1685a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        @Override
1686c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
1687969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1688c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1689c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
1690c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1691c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1692c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1693c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1694c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.getHardwareList();
1695c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1696c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1697c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1698c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1699c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1700c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1701c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware acquireTvInputHardware(int deviceId,
1702969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                ITvInputHardwareCallback callback, TvInputInfo info, int userId)
1703969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                throws RemoteException {
1704969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1705c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1706c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
1707c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1708c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1709c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1710c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
1711c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1712c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "acquireTvInputHardware");
1713c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1714c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.acquireHardware(
1715969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        deviceId, callback, info, callingUid, resolvedUserId);
1716c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1717c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1718c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1719c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1720c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1721c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1722c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
1723c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
1724969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1725c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1726c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
1727c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1728c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1729c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1730c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
1731c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1732c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "releaseTvInputHardware");
1733c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1734c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mTvInputHardwareManager.releaseHardware(
1735c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, hardware, callingUid, resolvedUserId);
1736c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1737c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1738c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1739c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1740e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1741e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        @Override
174258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        public List<DvbDeviceInfo> getDvbDeviceList() throws RemoteException {
174358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
174458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    != PackageManager.PERMISSION_GRANTED) {
174558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                throw new SecurityException("Requires DVB_DEVICE permission");
174658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            }
174758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
174858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            final long identity = Binder.clearCallingIdentity();
174958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            try {
1750ef1659f6249205e052c14fb607385b0e6b654223Jiabin                // Pattern1: /dev/dvb%d.frontend%d
1751ef1659f6249205e052c14fb607385b0e6b654223Jiabin                ArrayList<DvbDeviceInfo> deviceInfosFromPattern1 = new ArrayList<>();
175258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                File devDirectory = new File("/dev");
1753ef1659f6249205e052c14fb607385b0e6b654223Jiabin                boolean dvbDirectoryFound = false;
175458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                for (String fileName : devDirectory.list()) {
175558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    Matcher matcher = sFrontEndDevicePattern.matcher(fileName);
175658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    if (matcher.find()) {
175758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        int adapterId = Integer.parseInt(matcher.group(1));
175858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        int deviceId = Integer.parseInt(matcher.group(2));
1759ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        deviceInfosFromPattern1.add(new DvbDeviceInfo(adapterId, deviceId));
1760ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    }
1761ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    if (TextUtils.equals("dvb", fileName)) {
1762ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        dvbDirectoryFound = true;
1763ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    }
1764ef1659f6249205e052c14fb607385b0e6b654223Jiabin                }
1765ef1659f6249205e052c14fb607385b0e6b654223Jiabin                if (!dvbDirectoryFound) {
1766ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    return Collections.unmodifiableList(deviceInfosFromPattern1);
1767ef1659f6249205e052c14fb607385b0e6b654223Jiabin                }
1768ef1659f6249205e052c14fb607385b0e6b654223Jiabin                File dvbDirectory = new File(DVB_DIRECTORY);
1769ef1659f6249205e052c14fb607385b0e6b654223Jiabin                // Pattern2: /dev/dvb/adapter%d/frontend%d
1770ef1659f6249205e052c14fb607385b0e6b654223Jiabin                ArrayList<DvbDeviceInfo> deviceInfosFromPattern2 = new ArrayList<>();
1771ef1659f6249205e052c14fb607385b0e6b654223Jiabin                for (String fileNameInDvb : dvbDirectory.list()) {
1772ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
1773ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    if (adapterMatcher.find()) {
1774ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        int adapterId = Integer.parseInt(adapterMatcher.group(1));
1775ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
1776ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        for (String fileNameInAdapter : adapterDirectory.list()) {
1777ef1659f6249205e052c14fb607385b0e6b654223Jiabin                            Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
1778ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                    fileNameInAdapter);
1779ef1659f6249205e052c14fb607385b0e6b654223Jiabin                            if (frontendMatcher.find()) {
1780ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                int deviceId = Integer.parseInt(frontendMatcher.group(1));
1781ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                deviceInfosFromPattern2.add(
1782ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                        new DvbDeviceInfo(adapterId, deviceId));
1783ef1659f6249205e052c14fb607385b0e6b654223Jiabin                            }
1784ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        }
178558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    }
178658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                }
1787ef1659f6249205e052c14fb607385b0e6b654223Jiabin                return deviceInfosFromPattern2.isEmpty()
1788ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        ? Collections.unmodifiableList(deviceInfosFromPattern1)
1789ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        : Collections.unmodifiableList(deviceInfosFromPattern2);
179058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            } finally {
179158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                Binder.restoreCallingIdentity(identity);
179258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            }
179358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        }
179458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
179558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        @Override
179658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device)
179758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                throws RemoteException {
179858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
179958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    != PackageManager.PERMISSION_GRANTED) {
180058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                throw new SecurityException("Requires DVB_DEVICE permission");
180158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            }
180258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
1803ef1659f6249205e052c14fb607385b0e6b654223Jiabin            File devDirectory = new File("/dev");
1804ef1659f6249205e052c14fb607385b0e6b654223Jiabin            boolean dvbDeviceFound = false;
1805ef1659f6249205e052c14fb607385b0e6b654223Jiabin            for (String fileName : devDirectory.list()) {
1806ef1659f6249205e052c14fb607385b0e6b654223Jiabin                if (TextUtils.equals("dvb", fileName)) {
1807ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    File dvbDirectory = new File(DVB_DIRECTORY);
1808ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    for (String fileNameInDvb : dvbDirectory.list()) {
1809ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
1810ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        if (adapterMatcher.find()) {
1811ef1659f6249205e052c14fb607385b0e6b654223Jiabin                            File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
1812ef1659f6249205e052c14fb607385b0e6b654223Jiabin                            for (String fileNameInAdapter : adapterDirectory.list()) {
1813ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
1814ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                        fileNameInAdapter);
1815ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                if (frontendMatcher.find()) {
1816ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                    dvbDeviceFound = true;
1817ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                    break;
1818ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                }
1819ef1659f6249205e052c14fb607385b0e6b654223Jiabin                            }
1820ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        }
1821ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        if (dvbDeviceFound) {
1822ef1659f6249205e052c14fb607385b0e6b654223Jiabin                            break;
1823ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        }
1824ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    }
1825ef1659f6249205e052c14fb607385b0e6b654223Jiabin                }
1826ef1659f6249205e052c14fb607385b0e6b654223Jiabin                if (dvbDeviceFound) {
1827ef1659f6249205e052c14fb607385b0e6b654223Jiabin                    break;
1828ef1659f6249205e052c14fb607385b0e6b654223Jiabin                }
1829ef1659f6249205e052c14fb607385b0e6b654223Jiabin            }
1830ef1659f6249205e052c14fb607385b0e6b654223Jiabin
183158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            final long identity = Binder.clearCallingIdentity();
183258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            try {
183358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                String deviceFileName;
183458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                switch (device) {
183558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    case TvInputManager.DVB_DEVICE_DEMUX:
1836ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        deviceFileName = String.format(dvbDeviceFound
1837ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                ? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d",
1838ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                info.getAdapterId(), info.getDeviceId());
183958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        break;
184058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    case TvInputManager.DVB_DEVICE_DVR:
1841ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        deviceFileName = String.format(dvbDeviceFound
1842ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                ? "/dev/dvb/adapter%d/dvr%d" : "/dev/dvb%d.dvr%d",
1843ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                info.getAdapterId(), info.getDeviceId());
184458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        break;
184558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    case TvInputManager.DVB_DEVICE_FRONTEND:
1846ef1659f6249205e052c14fb607385b0e6b654223Jiabin                        deviceFileName = String.format(dvbDeviceFound
1847ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                ? "/dev/dvb/adapter%d/frontend%d" : "/dev/dvb%d.frontend%d",
1848ef1659f6249205e052c14fb607385b0e6b654223Jiabin                                info.getAdapterId(), info.getDeviceId());
184958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        break;
185058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    default:
185158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                        throw new IllegalArgumentException("Invalid DVB device: " + device);
185258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                }
185358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                try {
185458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    // The DVB frontend device only needs to be opened in read/write mode, which
185558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    // allows performing tuning operations. The DVB demux and DVR device are enough
185658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    // to be opened in read only mode.
185758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    return ParcelFileDescriptor.open(new File(deviceFileName),
185858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                            TvInputManager.DVB_DEVICE_FRONTEND == device
185958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                                    ? ParcelFileDescriptor.MODE_READ_WRITE
186058739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                                    : ParcelFileDescriptor.MODE_READ_ONLY);
186158739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                } catch (FileNotFoundException e) {
186258739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                    return null;
186358739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                }
186458739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            } finally {
186558739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung                Binder.restoreCallingIdentity(identity);
186658739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung            }
186758739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        }
186858739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung
186958739e758428f3b880f8e67161f57c59aa06d496Jaesung Chung        @Override
1870c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
1871c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                throws RemoteException {
1872c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (mContext.checkCallingPermission(
1873c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    android.Manifest.permission.CAPTURE_TV_INPUT)
1874c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    != PackageManager.PERMISSION_GRANTED) {
1875c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1876c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1877c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1878c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final long identity = Binder.clearCallingIdentity();
1879c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final int callingUid = Binder.getCallingUid();
1880c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1881c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    userId, "getAvailableTvStreamConfigList");
1882c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            try {
1883c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return mTvInputHardwareManager.getAvailableTvStreamConfigList(
1884c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        inputId, callingUid, resolvedUserId);
1885c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            } finally {
1886c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Binder.restoreCallingIdentity(identity);
1887c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1888c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
1889c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1890c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        @Override
1891c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
1892c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int userId)
1893c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                throws RemoteException {
1894c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (mContext.checkCallingPermission(
1895c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    android.Manifest.permission.CAPTURE_TV_INPUT)
1896c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    != PackageManager.PERMISSION_GRANTED) {
1897c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1898c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1899c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1900c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final long identity = Binder.clearCallingIdentity();
1901c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final int callingUid = Binder.getCallingUid();
1902c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1903c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    userId, "captureFrame");
1904c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            try {
1905bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                String hardwareInputId = null;
190679124a717c09f12c74d587d3977bf33ca37e6420Terry Heo                synchronized (mLock) {
19074f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1908bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    if (userState.inputMap.get(inputId) == null) {
1909fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                        Slog.e(TAG, "input not found for " + inputId);
1910bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        return false;
1911bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    }
1912bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    for (SessionState sessionState : userState.sessionStateMap.values()) {
19132cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                        if (sessionState.inputId.equals(inputId)
1914fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                && sessionState.hardwareSessionToken != null) {
1915bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            hardwareInputId = userState.sessionStateMap.get(
19162cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                                    sessionState.hardwareSessionToken).inputId;
1917bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                            break;
1918bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        }
1919bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                    }
192079124a717c09f12c74d587d3977bf33ca37e6420Terry Heo                }
1921c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return mTvInputHardwareManager.captureFrame(
1922bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang                        (hardwareInputId != null) ? hardwareInputId : inputId,
192379124a717c09f12c74d587d3977bf33ca37e6420Terry Heo                        surface, config, callingUid, resolvedUserId);
1924c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            } finally {
1925c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Binder.restoreCallingIdentity(identity);
1926c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1927c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
1928c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1929c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        @Override
1930df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo        public boolean isSingleSessionActive(int userId) throws RemoteException {
1931df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            final long identity = Binder.clearCallingIdentity();
1932df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            final int callingUid = Binder.getCallingUid();
1933df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1934df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    userId, "isSingleSessionActive");
1935df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            try {
1936df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                synchronized (mLock) {
19374f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1938df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    if (userState.sessionStateMap.size() == 1) {
1939df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                        return true;
194093ff14b7a5d1a9b4d3f57da85b286069fe9d8303Jae Seo                    } else if (userState.sessionStateMap.size() == 2) {
1941df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                        SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
194293ff14b7a5d1a9b4d3f57da85b286069fe9d8303Jae Seo                                new SessionState[2]);
1943df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                        // Check if there is a wrapper input.
1944fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        if (sessionStates[0].hardwareSessionToken != null
1945fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                || sessionStates[1].hardwareSessionToken != null) {
1946df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                            return true;
1947df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                        }
1948df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    }
1949df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                    return false;
1950df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                }
1951df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            } finally {
1952df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo                Binder.restoreCallingIdentity(identity);
1953df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo            }
1954df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo        }
1955df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo
1956df9f0a321e0cb2958c9d170395a0367a106fa0e6Terry Heo        @Override
19572e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang        public void requestChannelBrowsable(Uri channelUri, int userId)
19582e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                throws RemoteException {
19592e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang            final String callingPackageName = getCallingPackageName();
19602e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang            final long identity = Binder.clearCallingIdentity();
19612e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang            final int callingUid = Binder.getCallingUid();
19622e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
19632e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                userId, "requestChannelBrowsable");
19642e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang            try {
19652e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
19662e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                List<ResolveInfo> list = getContext().getPackageManager()
19672e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                    .queryBroadcastReceivers(intent, 0);
19682e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                if (list != null) {
19692e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                    for (ResolveInfo info : list) {
19702e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                        String receiverPackageName = info.activityInfo.packageName;
19712e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                        intent.putExtra(TvContract.EXTRA_CHANNEL_ID, ContentUris.parseId(
19722e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                                channelUri));
19732e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                        intent.putExtra(TvContract.EXTRA_PACKAGE_NAME, callingPackageName);
19742e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                        intent.setPackage(receiverPackageName);
19752e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                        getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
19762e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                    }
19772e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                }
19782e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang            } finally {
19792e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang                Binder.restoreCallingIdentity(identity);
19802e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang            }
19812e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang        }
19822e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang
19832e7f5ce709f9ee45c4fb219d768fbec057185375Dongwon Kang        @Override
19840f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo        @SuppressWarnings("resource")
1985e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1986e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
1987fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkey            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
1988e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1989e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            synchronized (mLock) {
1990e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.println("User Ids (Current user: " + mCurrentUserId + "):");
1991e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.increaseIndent();
1992e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                for (int i = 0; i < mUserStates.size(); i++) {
1993e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    int userId = mUserStates.keyAt(i);
1994e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println(Integer.valueOf(userId));
1995e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                }
1996e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.decreaseIndent();
1997e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1998e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                for (int i = 0; i < mUserStates.size(); i++) {
1999e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    int userId = mUserStates.keyAt(i);
20004f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                    UserState userState = getOrCreateUserStateLocked(userId);
2001e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("UserState (" + userId + "):");
2002e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
2003e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2004969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("inputMap: inputId -> TvInputState");
2005e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
20068e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                    for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) {
20078e6b51b0fb810ac990c863cc0579e2b2700ab7d6Jaewan Kim                        pw.println(entry.getKey() + ": " + entry.getValue());
2008e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
2009e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
2010e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2011969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("packageSet:");
2012e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
2013969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (String packageName : userState.packageSet) {
2014e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(packageName);
2015e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
2016e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
2017e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2018e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("clientStateMap: ITvInputClient -> ClientState");
2019e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
2020e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    for (Map.Entry<IBinder, ClientState> entry :
2021e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.clientStateMap.entrySet()) {
2022e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        ClientState client = entry.getValue();
2023e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + client);
2024e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2025e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
2026e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2027fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("sessionTokens:");
2028e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
2029fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        for (IBinder token : client.sessionTokens) {
2030e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            pw.println("" + token);
2031e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        }
2032e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
2033e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2034fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("clientTokens: " + client.clientToken);
2035fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("userId: " + client.userId);
2036e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2037e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
2038e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
2039e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
2040e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2041187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    pw.println("serviceStateMap: ComponentName -> ServiceState");
2042e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
2043187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    for (Map.Entry<ComponentName, ServiceState> entry :
2044e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.serviceStateMap.entrySet()) {
2045e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        ServiceState service = entry.getValue();
2046e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + service);
2047e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2048e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
2049e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2050fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("sessionTokens:");
2051e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
2052fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        for (IBinder token : service.sessionTokens) {
2053e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            pw.println("" + token);
2054e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        }
2055e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
2056e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2057fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("service: " + service.service);
2058fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("callback: " + service.callback);
2059fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("bound: " + service.bound);
2060fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("reconnecting: " + service.reconnecting);
2061e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2062e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
2063e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
2064e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
2065e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2066e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("sessionStateMap: ITvInputSession -> SessionState");
2067e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
2068e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    for (Map.Entry<IBinder, SessionState> entry :
2069e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.sessionStateMap.entrySet()) {
2070e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        SessionState session = entry.getValue();
2071e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + session);
2072e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2073e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
20742cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                        pw.println("inputId: " + session.inputId);
2075fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("client: " + session.client);
2076fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("seq: " + session.seq);
2077fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("callingUid: " + session.callingUid);
2078fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("userId: " + session.userId);
2079fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("sessionToken: " + session.sessionToken);
2080fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("session: " + session.session);
2081fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("logUri: " + session.logUri);
2082fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
2083e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
2084e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
2085e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
2086e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
2087969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("callbackSet:");
2088969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.increaseIndent();
2089969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (ITvInputManagerCallback callback : userState.callbackSet) {
2090969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        pw.println(callback.toString());
2091969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    }
2092969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.decreaseIndent();
2093969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2094956afc2ba79f50bb8025c6d334653e3c3419b480Ji-Hwan Lee                    pw.println("mainSessionToken: " + userState.mainSessionToken);
2095e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
2096e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                }
2097e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            }
20988d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            mTvInputHardwareManager.dump(fd, writer, args);
2099e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        }
21003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
21013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
21023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class UserState {
2103969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A mapping from the TV input id to its TvInputState.
21046e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private Map<String, TvInputState> inputMap = new HashMap<>();
21053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2106969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A set of all TV input packages.
21076e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Set<String> packageSet = new HashSet<>();
21085c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
21099c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        // A list of all TV content rating systems defined.
21109c165d6e9a2f085fbdc87b9221f2d52d851b2652Jae Seo        private final List<TvContentRatingSystemInfo>
21116e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                contentRatingSystemList = new ArrayList<>();
21125c5b83fcd58d21c9ab7ac986bf84f604ec5bb4b5Sungsoo Lim
211372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // A mapping from the token of a client to its state.
21146e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Map<IBinder, ClientState> clientStateMap = new HashMap<>();
211572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
21163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the name of a TV input service to its state.
21176e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>();
21183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
21193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the token of a TV input session to its state.
21206e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>();
2121969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2122969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A set of callbacks.
21236e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final Set<ITvInputManagerCallback> callbackSet = new HashSet<>();
212479124a717c09f12c74d587d3977bf33ca37e6420Terry Heo
21254c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        // The token of a "main" TV input session.
21264c52697dbed682a19dacc78b0c08931ea8dbc6b5Ji-Hwan Lee        private IBinder mainSessionToken = null;
2127783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
2128783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        // Persistent data store for all internal settings maintained by the TV input manager
2129783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        // service.
2130783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        private final PersistentDataStore persistentDataStore;
2131783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
2132783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        private UserState(Context context, int userId) {
2133783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            persistentDataStore = new PersistentDataStore(context, userId);
2134783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
21353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
21363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
213772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    private final class ClientState implements IBinder.DeathRecipient {
21386e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final List<IBinder> sessionTokens = new ArrayList<>();
213972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
2140fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private IBinder clientToken;
2141fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final int userId;
214272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
214372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState(IBinder clientToken, int userId) {
2144fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.clientToken = clientToken;
2145fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.userId = userId;
214672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
214772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
214872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        public boolean isEmpty() {
2149fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            return sessionTokens.isEmpty();
215072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
215172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
215272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        @Override
215372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        public void binderDied() {
215472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            synchronized (mLock) {
21554f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                UserState userState = getOrCreateUserStateLocked(userId);
215672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                // DO NOT remove the client state of clientStateMap in this method. It will be
2157a65118e13b5ceb54454b48f67ea754a38a08f27aJi-Hwan Lee                // removed in releaseSessionLocked().
2158fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                ClientState clientState = userState.clientStateMap.get(clientToken);
215972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                if (clientState != null) {
2160fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    while (clientState.sessionTokens.size() > 0) {
216172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        releaseSessionLocked(
2162fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
216372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    }
216472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                }
2165fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                clientToken = null;
216672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
216772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
216872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    }
216972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
21703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceState {
21716e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        private final List<IBinder> sessionTokens = new ArrayList<>();
2172fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final ServiceConnection connection;
2173fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final ComponentName component;
2174fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final boolean isHardware;
217571d5c76f19e8714102073bf774c025d5ccdebc11Shubang        private final Map<String, TvInputInfo> hardwareInputMap = new HashMap<>();
21763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2177fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private ITvInputService service;
2178fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private ServiceCallback callback;
2179fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private boolean bound;
2180fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private boolean reconnecting;
21813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
21829e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        private ServiceState(ComponentName component, int userId) {
2183fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.component = component;
2184fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.connection = new InputServiceConnection(component, userId);
2185fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
2186fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2187fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    }
2188fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2189fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private static final class TvInputState {
2190fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        // A TvInputInfo object which represents the TV input.
2191fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private TvInputInfo info;
2192fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2193fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        // The state of TV input. Connected by default.
2194fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private int state = INPUT_STATE_CONNECTED;
2195fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2196fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2197fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public String toString() {
2198fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            return "info: " + info + "; state: " + state;
21993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
22003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
22013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
22022b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private final class SessionState implements IBinder.DeathRecipient {
22032cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo        private final String inputId;
22042cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo        private final ComponentName componentName;
2205a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        private final boolean isRecordingSession;
2206fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final ITvInputClient client;
2207fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final int seq;
2208fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final int callingUid;
2209fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final int userId;
2210fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final IBinder sessionToken;
2211fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private ITvInputSession session;
2212fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private Uri logUri;
2213bd2fa2c02d916a9b6c62f8fd8701d779c00bd68dDongwon Kang        // Not null if this session represents an external device connected to a hardware TV input.
2214fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private IBinder hardwareSessionToken;
22153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
22162cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo        private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
22172cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
22182cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                int userId) {
2219fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.sessionToken = sessionToken;
22202cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo            this.inputId = inputId;
22212cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo            this.componentName = componentName;
2222a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            this.isRecordingSession = isRecordingSession;
2223fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.client = client;
2224fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.seq = seq;
2225fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.callingUid = callingUid;
2226fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            this.userId = userId;
22272b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
22282b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
22292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        @Override
22302b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void binderDied() {
22312b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
2232fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                session = null;
22338049f20df8c10397aac8d31bcb455913ad02bb67shubang                clearSessionAndNotifyClientLocked(this);
22342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
22353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
22363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
22373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
22383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class InputServiceConnection implements ServiceConnection {
22399e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        private final ComponentName mComponent;
22403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
22413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
22429e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        private InputServiceConnection(ComponentName component, int userId) {
22439e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            mComponent = component;
22443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
22453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
22463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
22473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
22489e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        public void onServiceConnected(ComponentName component, IBinder service) {
22493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
22509e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                Slog.d(TAG, "onServiceConnected(component=" + component + ")");
22513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
22523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
225381e3c3e68d3baf506a76e588e8f25ff69e419171Dongwon Kang                UserState userState = mUserStates.get(mUserId);
225481e3c3e68d3baf506a76e588e8f25ff69e419171Dongwon Kang                if (userState == null) {
225581e3c3e68d3baf506a76e588e8f25ff69e419171Dongwon Kang                    // The user was removed while connecting.
225681e3c3e68d3baf506a76e588e8f25ff69e419171Dongwon Kang                    mContext.unbindService(this);
225781e3c3e68d3baf506a76e588e8f25ff69e419171Dongwon Kang                    return;
225881e3c3e68d3baf506a76e588e8f25ff69e419171Dongwon Kang                }
22599e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                ServiceState serviceState = userState.serviceStateMap.get(mComponent);
2260fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                serviceState.service = ITvInputService.Stub.asInterface(service);
22613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
22623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // Register a callback, if we need to.
2263fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (serviceState.isHardware && serviceState.callback == null) {
2264fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.callback = new ServiceCallback(mComponent, mUserId);
22653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
2266fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.registerCallback(serviceState.callback);
22673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
22689a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in registerCallback", e);
22693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
22703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
22713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
22723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // And create sessions, if any.
2273fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                for (IBinder sessionToken : serviceState.sessionTokens) {
2274fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
22753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2276969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2277187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                for (TvInputState inputState : userState.inputMap.values()) {
2278fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (inputState.info.getComponent().equals(component)
227982fce64530d19a4da1c02d424fb2515feafe6a70Jae Seo                            && inputState.state != INPUT_STATE_CONNECTED) {
2280fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        notifyInputStateChangedLocked(userState, inputState.info.getId(),
2281fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                                inputState.state, null);
2282187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2283187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
2284187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2285fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (serviceState.isHardware) {
228671d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    serviceState.hardwareInputMap.clear();
2287c980f43d940f1154a15ef003fb00c19708c396daJae Seo                    for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) {
2288187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        try {
2289c980f43d940f1154a15ef003fb00c19708c396daJae Seo                            serviceState.service.notifyHardwareAdded(hardware);
2290187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        } catch (RemoteException e) {
2291187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                            Slog.e(TAG, "error in notifyHardwareAdded", e);
2292187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        }
2293187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2294c980f43d940f1154a15ef003fb00c19708c396daJae Seo                    for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
22954f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        try {
2296c980f43d940f1154a15ef003fb00c19708c396daJae Seo                            serviceState.service.notifyHdmiDeviceAdded(device);
22974f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        } catch (RemoteException e) {
2298546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                            Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
22994f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        }
23004f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    }
2301969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
23023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
23033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
23043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
23053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
23069e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        public void onServiceDisconnected(ComponentName component) {
23073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
23089e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                Slog.d(TAG, "onServiceDisconnected(component=" + component + ")");
23093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
23109e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            if (!mComponent.equals(component)) {
23112b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                throw new IllegalArgumentException("Mismatched ComponentName: "
23129e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                        + mComponent + " (expected), " + component + " (actual).");
23132b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
23142b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
23154f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                UserState userState = getOrCreateUserStateLocked(mUserId);
23169e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                ServiceState serviceState = userState.serviceStateMap.get(mComponent);
23172b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState != null) {
2318fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.reconnecting = true;
2319fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.bound = false;
2320fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.service = null;
2321fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    serviceState.callback = null;
23222b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
2323426c9a4008b75b93cbfea15aa4b8c47c5fdb49b9Dongwon Kang                    abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
23242b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
23252b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
23263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
23273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
23283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
23293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
23309e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        private final ComponentName mComponent;
23313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
23323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
23339e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee        ServiceCallback(ComponentName component, int userId) {
23349e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            mComponent = component;
23353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
23363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
23373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
23384f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        private void ensureHardwarePermission() {
23394f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
23404f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    != PackageManager.PERMISSION_GRANTED) {
23414f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new SecurityException("The caller does not have hardware permission");
23424f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
23434f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
23444f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
23454f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        private void ensureValidInput(TvInputInfo inputInfo) {
23469e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) {
23474f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new IllegalArgumentException("Invalid TvInputInfo");
23484f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
23494f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
23504f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
23511abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        private void addHardwareInputLocked(TvInputInfo inputInfo) {
23529e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee            ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
235371d5c76f19e8714102073bf774c025d5ccdebc11Shubang            serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
235419ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee            buildTvInputListLocked(mUserId, null);
23554f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
23564f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
23571abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        public void addHardwareInput(int deviceId, TvInputInfo inputInfo) {
23584f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureHardwarePermission();
23594f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureValidInput(inputInfo);
2360187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
23611abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
23621abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                addHardwareInputLocked(inputInfo);
23634f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
23644f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2365187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
23661abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        public void addHdmiInput(int id, TvInputInfo inputInfo) {
23674f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureHardwarePermission();
23684f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureValidInput(inputInfo);
23694f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            synchronized (mLock) {
23701abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                mTvInputHardwareManager.addHdmiInput(id, inputInfo);
23711abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                addHardwareInputLocked(inputInfo);
23723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2373187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
2374187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
23751abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo        public void removeHardwareInput(String inputId) {
23764f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            ensureHardwarePermission();
23773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
23789e8ade2eb7ee835963473c9cf6faaf5423b0b048Ji-Hwan Lee                ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
237971d5c76f19e8714102073bf774c025d5ccdebc11Shubang                boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
2380187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                if (removed) {
238119ba61affbc0c4a4454abc6cf09f70ea428d1a62Chulwoo Lee                    buildTvInputListLocked(mUserId, null);
23821abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo                    mTvInputHardwareManager.removeHardwareInput(inputId);
2383187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                } else {
2384fea8dd45f4955b4b4b6536bf51453e19288deba2Jae Seo                    Slog.e(TAG, "failed to remove input " + inputId);
2385187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
23863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
23873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
23883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
238931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
2390fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private final class SessionCallback extends ITvInputSessionCallback.Stub {
23919c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee        private final SessionState mSessionState;
2392fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        private final InputChannel[] mChannels;
2393fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2394fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        SessionCallback(SessionState sessionState, InputChannel[] channels) {
23959c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            mSessionState = sessionState;
2396fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            mChannels = channels;
2397fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2398fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2399fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
24006e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) {
2401fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            if (DEBUG) {
24022cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")");
2403fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2404fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
24059c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                mSessionState.session = session;
24066e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                mSessionState.hardwareSessionToken = hardwareSessionToken;
24079c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (session != null && addSessionTokenToClientStateLocked(session)) {
24089c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    sendSessionTokenToClientLocked(mSessionState.client,
24092cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                            mSessionState.inputId, mSessionState.sessionToken, mChannels[0],
24109c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                            mSessionState.seq);
2411fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } else {
24129c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId);
24139c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    sendSessionTokenToClientLocked(mSessionState.client,
24142cdb05e576d5355d444a9533a62e6892cc02f9f2Jae Seo                            mSessionState.inputId, null, null, mSessionState.seq);
24159c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                }
24169c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                mChannels[0].dispose();
24179c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            }
24189c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee        }
2419fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
24209c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee        private boolean addSessionTokenToClientStateLocked(ITvInputSession session) {
24219c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            try {
24229c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                session.asBinder().linkToDeath(mSessionState, 0);
24239c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            } catch (RemoteException e) {
24249c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                Slog.e(TAG, "session process has already died", e);
24259c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                return false;
24269c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            }
2427fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
24289c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            IBinder clientToken = mSessionState.client.asBinder();
24294f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo            UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
24309c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            ClientState clientState = userState.clientStateMap.get(clientToken);
24319c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            if (clientState == null) {
24329c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                clientState = new ClientState(clientToken, mSessionState.userId);
24339c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                try {
24349c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    clientToken.linkToDeath(clientState, 0);
24359c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                } catch (RemoteException e) {
24369c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    Slog.e(TAG, "client process has already died", e);
24379c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    return false;
2438fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
24399c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                userState.clientStateMap.put(clientToken, clientState);
2440fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
24419c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            clientState.sessionTokens.add(mSessionState.sessionToken);
24429c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee            return true;
2443fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2444fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2445fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2446fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onChannelRetuned(Uri channelUri) {
2447fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2448fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2449fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
2450fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
24519c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2452fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2453fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2454fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
2455fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    // TODO: Consider adding this channel change in the watch log. When we do
2456fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    // that, how we can protect the watch log from malicious tv inputs should
2457fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    // be addressed. e.g. add a field which represents where the channel change
2458fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    // originated from.
24599c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq);
2460fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2461fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onChannelRetuned", e);
2462fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2463fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2464fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2465fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2466fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2467fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onTracksChanged(List<TvTrackInfo> tracks) {
2468fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2469fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2470fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onTracksChanged(" + tracks + ")");
2471fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
24729c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2473fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2474fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2475fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
24769c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onTracksChanged(tracks, mSessionState.seq);
2477fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2478fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onTracksChanged", e);
2479fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2480fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2481fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2482fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2483fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2484fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onTrackSelected(int type, String trackId) {
2485fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2486fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2487fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
2488fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
24899c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2490fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2491fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2492fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
24939c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq);
2494fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2495fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onTrackSelected", e);
2496fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2497fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2498fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2499fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2500fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2501fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onVideoAvailable() {
2502fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2503fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2504fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onVideoAvailable()");
2505fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
25069c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2507fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2508fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2509fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
25109c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onVideoAvailable(mSessionState.seq);
2511fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2512fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onVideoAvailable", e);
2513fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2514fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2515fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2516fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2517fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2518fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onVideoUnavailable(int reason) {
2519fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2520fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2521fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
2522fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
25239c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2524fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2525fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2526fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
25279c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
2528fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2529fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onVideoUnavailable", e);
2530fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2531fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2532fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2533fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2534fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2535fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onContentAllowed() {
2536fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2537fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2538fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onContentAllowed()");
2539fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
25409c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2541fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2542fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2543fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
25449c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onContentAllowed(mSessionState.seq);
2545fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2546fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onContentAllowed", e);
2547fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2548fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2549fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2550fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2551fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2552fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onContentBlocked(String rating) {
2553fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2554fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2555fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onContentBlocked()");
2556fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
25579c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2558fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2559fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2560fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
25619c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onContentBlocked(rating, mSessionState.seq);
2562fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2563fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onContentBlocked", e);
2564fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2565fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2566fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2567fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2568fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2569fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onLayoutSurface(int left, int top, int right, int bottom) {
2570fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2571fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
2572fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
2573fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                            + ", right=" + right + ", bottom=" + bottom + ",)");
2574fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
25759c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2576fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2577fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2578fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
25799c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onLayoutSurface(left, top, right, bottom,
25809c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                            mSessionState.seq);
2581fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2582fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onLayoutSurface", e);
2583fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2584fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2585fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
2586fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2587fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        @Override
2588fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        public void onSessionEvent(String eventType, Bundle eventArgs) {
2589fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            synchronized (mLock) {
2590fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                if (DEBUG) {
25914eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                    Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs
25924eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                            + ")");
2593fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
25949c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                if (mSessionState.session == null || mSessionState.client == null) {
2595fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    return;
2596fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2597fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                try {
25989c6b5b729bd83b1d1e00428f8a76f272b609c97eJi-Hwan Lee                    mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq);
2599fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                } catch (RemoteException e) {
2600fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    Slog.e(TAG, "error in onSessionEvent", e);
2601fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                }
2602fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            }
2603fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang        }
26046f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
26056f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
26066f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftStatusChanged(int status) {
26076f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            synchronized (mLock) {
26086f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (DEBUG) {
26094eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                    Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")");
26106f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26116f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (mSessionState.session == null || mSessionState.client == null) {
26126f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    return;
26136f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26146f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                try {
26156f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq);
26166f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                } catch (RemoteException e) {
26176f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.e(TAG, "error in onTimeShiftStatusChanged", e);
26186f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26196f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
26206f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
26216f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
26226f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
26236f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftStartPositionChanged(long timeMs) {
26246f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            synchronized (mLock) {
26256f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (DEBUG) {
26264eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                    Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")");
26276f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26286f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (mSessionState.session == null || mSessionState.client == null) {
26296f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    return;
26306f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26316f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                try {
26326f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq);
26336f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                } catch (RemoteException e) {
26346f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e);
26356f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26366f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
26376f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
26386f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang
26396f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        @Override
26406f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        public void onTimeShiftCurrentPositionChanged(long timeMs) {
26416f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            synchronized (mLock) {
26426f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (DEBUG) {
26434eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                    Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")");
26446f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26456f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                if (mSessionState.session == null || mSessionState.client == null) {
26466f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    return;
26476f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26486f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                try {
26496f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs,
26506f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                            mSessionState.seq);
26516f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                } catch (RemoteException e) {
26526f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                    Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e);
26536f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang                }
26546f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang            }
26556f0240cf63fe62b0af2c7d5112f9881d1e167bfcDongwon Kang        }
2656a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
2657a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        // For the recording session only
2658a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        @Override
2659b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang        public void onTuned(Uri channelUri) {
2660a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            synchronized (mLock) {
2661a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                if (DEBUG) {
2662e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo                    Slog.d(TAG, "onTuned()");
2663a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2664a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                if (mSessionState.session == null || mSessionState.client == null) {
2665a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    return;
2666a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2667a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                try {
2668b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang                    mSessionState.client.onTuned(mSessionState.seq, channelUri);
2669a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                } catch (RemoteException e) {
2670e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo                    Slog.e(TAG, "error in onTuned", e);
2671a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2672a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
2673a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
2674a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
2675a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        // For the recording session only
2676a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        @Override
2677a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public void onRecordingStopped(Uri recordedProgramUri) {
2678a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            synchronized (mLock) {
2679a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                if (DEBUG) {
26804eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                    Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri
26814eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                            + ")");
2682a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2683a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                if (mSessionState.session == null || mSessionState.client == null) {
2684a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    return;
2685a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2686a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                try {
2687a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq);
2688a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                } catch (RemoteException e) {
2689a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    Slog.e(TAG, "error in onRecordingStopped", e);
2690a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2691a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
2692a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
2693a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo
2694a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        // For the recording session only
2695a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        @Override
2696a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        public void onError(int error) {
2697a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            synchronized (mLock) {
2698a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                if (DEBUG) {
26994eee6a73e476cd2d82a69f3a535628901047f140Jae Seo                    Slog.d(TAG, "onError(error=" + error + ")");
2700a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2701a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                if (mSessionState.session == null || mSessionState.client == null) {
2702a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    return;
2703a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2704a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                try {
2705a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    mSessionState.client.onError(error, mSessionState.seq);
2706a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                } catch (RemoteException e) {
2707a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                    Slog.e(TAG, "error in onError", e);
2708a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo                }
2709a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo            }
2710a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo        }
2711fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    }
2712fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
2713fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private static final class WatchLogHandler extends Handler {
27147eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // There are only two kinds of watch events that can happen on the system:
27157eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // 1. The current TV input session is tuned to a new channel.
27167eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // 2. The session is released for some reason.
27177eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // The former indicates the end of the previous log entry, if any, followed by the start of
27187eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // a new entry. The latter indicates the end of the most recent entry for the given session.
27197eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // Here the system supplies the database the smallest set of information only that is
27207eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // sufficient to consolidate the log entries while minimizing database operations in the
27217eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        // system service.
27228c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        static final int MSG_LOG_WATCH_START = 1;
27238c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        static final int MSG_LOG_WATCH_END = 2;
27248c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        static final int MSG_SWITCH_CONTENT_RESOLVER = 3;
27257eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
27268c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        private ContentResolver mContentResolver;
2727fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang
27288c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo        WatchLogHandler(ContentResolver contentResolver, Looper looper) {
272931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            super(looper);
2730fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang            mContentResolver = contentResolver;
273131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
273231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
273331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        @Override
273431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public void handleMessage(Message msg) {
273531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            switch (msg.what) {
27367eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                case MSG_LOG_WATCH_START: {
273731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
27387eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    String packageName = (String) args.arg1;
27397eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    long watchStartTime = (long) args.arg2;
27407eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    long channelId = (long) args.arg3;
27417eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    Bundle tuneParams = (Bundle) args.arg4;
27427eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    IBinder sessionToken = (IBinder) args.arg5;
27437eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
27447eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    ContentValues values = new ContentValues();
27457eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
27467eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
27477eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                            watchStartTime);
27487eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
27497eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    if (tuneParams != null) {
27507eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS,
27517eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                                encodeTuneParams(tuneParams));
27527eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    }
27537eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
27547eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                            sessionToken.toString());
27557eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
27567eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
275731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
27588c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    break;
275931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
27607eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                case MSG_LOG_WATCH_END: {
276131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
27627eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    IBinder sessionToken = (IBinder) args.arg1;
27637eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    long watchEndTime = (long) args.arg2;
27647eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
27657eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    ContentValues values = new ContentValues();
27667eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
27677eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                            watchEndTime);
27687eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
27697eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                            sessionToken.toString());
27707eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo
27717eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
277231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
27738c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    break;
27748c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                }
27758c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                case MSG_SWITCH_CONTENT_RESOLVER: {
27768c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    mContentResolver = (ContentResolver) msg.obj;
27778c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    break;
277831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
277931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                default: {
27808c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    Slog.w(TAG, "unhandled message code: " + msg.what);
27818c375feb686dee8b6e8a9c69100de88c4d61afdcJae Seo                    break;
278231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
278331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
278431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
278531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
27867eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        private String encodeTuneParams(Bundle tuneParams) {
27877eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            StringBuilder builder = new StringBuilder();
27887eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            Set<String> keySet = tuneParams.keySet();
27897eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            Iterator<String> it = keySet.iterator();
27907eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            while (it.hasNext()) {
27917eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                String key = it.next();
27927eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                Object value = tuneParams.get(key);
27937eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                if (value == null) {
27947eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    continue;
279531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
27967eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                builder.append(replaceEscapeCharacters(key));
27977eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                builder.append("=");
27987eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                builder.append(replaceEscapeCharacters(value.toString()));
27997eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                if (it.hasNext()) {
28007eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    builder.append(", ");
280131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
280231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
28037eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            return builder.toString();
280431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
280531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
28067eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo        private String replaceEscapeCharacters(String src) {
28077eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            final char ESCAPE_CHARACTER = '%';
28087eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            final String ENCODING_TARGET_CHARACTERS = "%=,";
28097eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            StringBuilder builder = new StringBuilder();
28107eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            for (char ch : src.toCharArray()) {
28117eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) {
28127eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                    builder.append(ESCAPE_CHARACTER);
2813579befecb248162021929ab58ffd23f1724cc6beJae Seo                }
28147eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo                builder.append(ch);
2815579befecb248162021929ab58ffd23f1724cc6beJae Seo            }
28167eb75dff7a0fb4b19c3e801cd388483d7d471f41Jae Seo            return builder.toString();
2817579befecb248162021929ab58ffd23f1724cc6beJae Seo        }
281831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    }
2819969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2820fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang    private final class HardwareListener implements TvInputHardwareManager.Listener {
2821187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2822187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onStateChanged(String inputId, int state) {
2823969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            synchronized (mLock) {
2824969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                setStateLocked(inputId, state, mCurrentUserId);
2825969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2826969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
2827187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2828187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2829187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
2830187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
28314f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
2832187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                // Broadcast the event to all hardware inputs.
2833187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2834fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (!serviceState.isHardware || serviceState.service == null) continue;
2835187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    try {
2836fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.notifyHardwareAdded(info);
2837187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    } catch (RemoteException e) {
2838187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        Slog.e(TAG, "error in notifyHardwareAdded", e);
2839187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2840187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
2841187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
2842187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
2843187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2844187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
28454f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
2846187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
28474f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
2848187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                // Broadcast the event to all hardware inputs.
2849187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2850fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (!serviceState.isHardware || serviceState.service == null) continue;
2851187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    try {
2852fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.notifyHardwareRemoved(info);
2853187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    } catch (RemoteException e) {
2854187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                        Slog.e(TAG, "error in notifyHardwareRemoved", e);
2855187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    }
2856187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
2857187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
2858187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
2859187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2860187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2861546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
2862187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
28634f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
28644f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                // Broadcast the event to all hardware inputs.
28654f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2866fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (!serviceState.isHardware || serviceState.service == null) continue;
28674f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    try {
2868fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
28694f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    } catch (RemoteException e) {
2870546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
28714f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    }
28724f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                }
2873187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
2874187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
2875187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
2876187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
2877546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
2878187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            synchronized (mLock) {
28794f1a6d477c0da5948617a1207c319ca227f60e57Jae Seo                UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
28804f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                // Broadcast the event to all hardware inputs.
28814f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2882fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                    if (!serviceState.isHardware || serviceState.service == null) continue;
28834f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    try {
2884fd8aa02d73ed43fb90bf44dbe4d65d378261d905Dongwon Kang                        serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
28854f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    } catch (RemoteException e) {
2886546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
28874f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    }
28884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                }
2889187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            }
2890187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
289161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang
289261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        @Override
2893e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim        public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
2894e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim            synchronized (mLock) {
28956e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                Integer state;
2896e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                switch (deviceInfo.getDevicePowerStatus()) {
2897e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_ON:
2898e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        state = INPUT_STATE_CONNECTED;
2899e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        break;
2900e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_STANDBY:
2901e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
2902e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
2903e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        state = INPUT_STATE_CONNECTED_STANDBY;
2904e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        break;
2905e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    case HdmiControlManager.POWER_STATUS_UNKNOWN:
2906e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    default:
2907e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        state = null;
2908e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        break;
2909e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                }
2910e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                if (state != null) {
29116e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    setStateLocked(inputId, state, mCurrentUserId);
2912e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                }
2913e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim            }
291461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        }
2915969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
2916fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang
2917fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang    private static class SessionNotFoundException extends IllegalArgumentException {
2918fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        public SessionNotFoundException(String name) {
2919fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang            super(name);
2920fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang        }
2921fdce9e541e143332ba4ece6a0bda4f11520f0b07Dongwon Kang    }
29223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
2923