TvInputManagerService.java revision e821d711db1799dc51661a3ed6188f3cd942bae7
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;
20969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
21969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.app.ActivityManager;
233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.BroadcastReceiver;
243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.ComponentName;
255c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport android.content.ContentProviderOperation;
265c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport android.content.ContentProviderResult;
2731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentResolver;
2831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentUris;
2931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentValues;
303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Context;
313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Intent;
323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.IntentFilter;
335c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport android.content.OperationApplicationException;
343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.ServiceConnection;
353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.PackageManager;
363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.ResolveInfo;
373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.ServiceInfo;
3831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.database.Cursor;
399a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.graphics.Rect;
40d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputClient;
41d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
42d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
43d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputManager;
44969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.ITvInputManagerCallback;
45d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputService;
46d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputServiceCallback;
47d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputSession;
48d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputSessionCallback;
49d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvContract;
50d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputHardwareInfo;
51d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputInfo;
52d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputService;
531f213914c45c23c653f721690da2ce0718e63139Dongwon Kangimport android.media.tv.TvTrackInfo;
543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.net.Uri;
553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Binder;
56832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Choimport android.os.Bundle;
5731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Handler;
583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.IBinder;
5931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Looper;
6031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Message;
613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Process;
623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.RemoteException;
633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.UserHandle;
649a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.util.Slog;
653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.util.SparseArray;
666a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputChannel;
673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.view.Surface;
683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.internal.content.PackageMonitor;
7031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.internal.os.SomeArgs;
71e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport com.android.internal.util.IndentingPrintWriter;
7231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.server.IoThread;
733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.server.SystemService;
743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
75e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Leeimport org.xmlpull.v1.XmlPullParserException;
76e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee
77e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport java.io.FileDescriptor;
78e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Leeimport java.io.IOException;
79e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kimimport java.io.PrintWriter;
803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.ArrayList;
813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.HashMap;
825c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport java.util.HashSet;
833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.List;
843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.Map;
855c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seoimport java.util.Set;
863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo/** This class provides a system service that manages television inputs. */
883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seopublic final class TvInputManagerService extends SystemService {
893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // STOPSHIP: Turn debugging off.
903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final boolean DEBUG = true;
913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final String TAG = "TvInputManagerService";
923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Context mContext;
94c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHardwareManager mTvInputHardwareManager;
953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final ContentResolver mContentResolver;
9731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A global lock.
993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Object mLock = new Object();
1003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // ID of the current user.
1023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int mCurrentUserId = UserHandle.USER_OWNER;
1033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A map from user id to UserState.
1053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
1063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final Handler mLogHandler;
10831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public TvInputManagerService(Context context) {
1103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        super(context);
11131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext = context;
11331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        mContentResolver = context.getContentResolver();
11431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        mLogHandler = new LogHandler(IoThread.get().getLooper());
11531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
116969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        mTvInputHardwareManager = new TvInputHardwareManager(context, new Client());
11731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(mCurrentUserId, new UserState());
1203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    @Override
1243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public void onStart() {
1253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
1263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1280ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    @Override
1290ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    public void onBootPhase(int phase) {
1300ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1310ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            registerBroadcastReceivers();
1320ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            synchronized (mLock) {
1330ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee                buildTvInputListLocked(mCurrentUserId);
1340ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            }
1350ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee        }
136969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        mTvInputHardwareManager.onBootPhase(phase);
1370ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    }
1380ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee
1393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void registerBroadcastReceivers() {
1403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageMonitor monitor = new PackageMonitor() {
1413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSomePackagesChanged() {
1433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
1443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    buildTvInputListLocked(mCurrentUserId);
1453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1475c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
1485c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            @Override
1495c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            public void onPackageRemoved(String packageName, int uid) {
1505c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                synchronized (mLock) {
1515c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    UserState userState = getUserStateLocked(mCurrentUserId);
152969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    if (!userState.packageSet.contains(packageName)) {
1535c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        // Not a TV input package.
1545c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        return;
1555c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    }
1565c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                }
1575c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
1585c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                ArrayList<ContentProviderOperation> operations =
1595c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        new ArrayList<ContentProviderOperation>();
1605c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
1615c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?";
1625c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                String[] selectionArgs = { packageName };
1635c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
1645c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI)
1655c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        .withSelection(selection, selectionArgs).build());
1665c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI)
1675c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        .withSelection(selection, selectionArgs).build());
1685c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                operations.add(ContentProviderOperation
1695c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        .newDelete(TvContract.WatchedPrograms.CONTENT_URI)
1705c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        .withSelection(selection, selectionArgs).build());
1715c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
1725c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                ContentProviderResult[] results = null;
1735c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                try {
1745c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
1755c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                } catch (RemoteException | OperationApplicationException e) {
1765c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    Slog.e(TAG, "error in applyBatch" + e);
1775c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                }
1785c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
1795c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                if (DEBUG) {
1805c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid
1815c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                            + ")");
1825c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    Slog.d(TAG, "results=" + results);
1835c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                }
1845c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            }
1853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
1863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        monitor.register(mContext, null, UserHandle.ALL, true);
1873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        IntentFilter intentFilter = new IntentFilter();
1893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
1903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
1913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext.registerReceiverAsUser(new BroadcastReceiver() {
1923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onReceive(Context context, Intent intent) {
1943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                String action = intent.getAction();
1953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
1963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
1983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }, UserHandle.ALL, intentFilter, null, null);
2023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void buildTvInputListLocked(int userId) {
2053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
206969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
207969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        Map<String, TvInputState> oldInputMap = userState.inputMap;
208969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        userState.inputMap = new HashMap<String, TvInputState>();
209969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
210969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        userState.packageSet.clear();
2113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2129a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        if (DEBUG) Slog.d(TAG, "buildTvInputList");
2133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageManager pm = mContext.getPackageManager();
2143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        List<ResolveInfo> services = pm.queryIntentServices(
215e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                new Intent(TvInputService.SERVICE_INTERFACE),
216e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
2173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        for (ResolveInfo ri : services) {
2183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            ServiceInfo si = ri.serviceInfo;
2193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
2209a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
2213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + android.Manifest.permission.BIND_TV_INPUT);
2223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                continue;
2233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
224e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            try {
225e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
226e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                if (DEBUG) Slog.d(TAG, "add " + info.getId());
227969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputState state = oldInputMap.get(info.getId());
228969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                if (state == null) {
229969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    state = new TvInputState();
230969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
231969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                userState.inputMap.put(info.getId(), state);
232969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                state.mInfo = info;
233969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                userState.packageSet.add(si.packageName);
234226a51958d645a8e2be3e40a6b6daaca558b4913Jaewan Kim
235226a51958d645a8e2be3e40a6b6daaca558b4913Jaewan Kim                // Reconnect the service if existing input is updated.
236226a51958d645a8e2be3e40a6b6daaca558b4913Jaewan Kim                updateServiceConnectionLocked(info.getId(), userId);
237e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            } catch (IOException | XmlPullParserException e) {
238e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                Slog.e(TAG, "Can't load TV input " + si.name, e);
239e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            }
2403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
241969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        oldInputMap.clear();
2423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void switchUser(int userId) {
2453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
2463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (mCurrentUserId == userId) {
2473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
2483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // final int oldUserId = mCurrentUserId;
2503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // TODO: Release services and sessions in the old user state, if needed.
2513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mCurrentUserId = userId;
2523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            UserState userState = mUserStates.get(userId);
2543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (userState == null) {
2553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                userState = new UserState();
2563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(userId, userState);
2583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            buildTvInputListLocked(userId);
2593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void removeUser(int userId) {
2633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
264b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            UserState userState = mUserStates.get(userId);
265b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            if (userState == null) {
266b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo                return;
267b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            }
2683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Release created sessions.
2693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (SessionState state : userState.sessionStateMap.values()) {
270d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (state.mSession != null) {
2713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
272d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        state.mSession.release();
2733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
2749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in release", e);
2753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.sessionStateMap.clear();
2793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Unregister all callbacks and unbind all services.
2813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (ServiceState serviceState : userState.serviceStateMap.values()) {
282d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (serviceState.mCallback != null) {
2833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
284d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.unregisterCallback(serviceState.mCallback);
2853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
2869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
2873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
28972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                serviceState.mClientTokens.clear();
290d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                mContext.unbindService(serviceState.mConnection);
2913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.serviceStateMap.clear();
2933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
29472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            userState.clientStateMap.clear();
29572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
2963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.remove(userId);
2973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private UserState getUserStateLocked(int userId) {
3013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = mUserStates.get(userId);
3023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (userState == null) {
3033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("User state not found for user ID " + userId);
3043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return userState;
3063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
308d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private ServiceState getServiceStateLocked(String inputId, int userId) {
3093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
310d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
3113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
312d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            throw new IllegalStateException("Service state not found for " + inputId + " (userId="
3137de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    + userId + ")");
3143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return serviceState;
3163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3182b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
3193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
3203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
3213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (sessionState == null) {
3223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalArgumentException("Session state not found for token " + sessionToken);
3233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Only the application that requested this session or the system can access it.
325d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
3263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new SecurityException("Illegal access to the session with token " + sessionToken
3273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + " from uid " + callingUid);
3283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        return sessionState;
3302b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
3312b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
3322b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
3332b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
334d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ITvInputSession session = sessionState.mSession;
3353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (session == null) {
3363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("Session not yet created for token " + sessionToken);
3373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return session;
3393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
3423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            String methodName) {
3433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
3443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                false, methodName, null);
3453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
347d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void updateServiceConnectionLocked(String inputId, int userId) {
3483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
349d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
3503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
3513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return;
3523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3532b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (serviceState.mReconnecting) {
3542b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!serviceState.mSessionTokens.isEmpty()) {
3552b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                // wait until all the sessions are removed.
3562b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                return;
3572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
3582b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            serviceState.mReconnecting = false;
3592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
36072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
361d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                && serviceState.mSessionTokens.isEmpty();
362d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
3633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is not yet connected but its state indicates that we
3643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // have pending requests. Then, connect the service.
365d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            if (serviceState.mBound) {
3663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // We have already bound to the service so we don't try to bind again until after we
3673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // unbind later on.
3683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
3693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
3703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
371d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId
3723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + ")");
3733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
374d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim
375d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
376969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    userState.inputMap.get(inputId).mInfo.getComponent());
377226a51958d645a8e2be3e40a6b6daaca558b4913Jaewan Kim            // Binding service may fail if the service is updating.
378226a51958d645a8e2be3e40a6b6daaca558b4913Jaewan Kim            // In that case, the connection will be revived in buildTvInputListLocked called by
379226a51958d645a8e2be3e40a6b6daaca558b4913Jaewan Kim            // onSomePackagesChanged.
380e17b2dd7bcc137bf4d842a779e8d62c63957a978Ji-Hwan Lee            serviceState.mBound = mContext.bindServiceAsUser(
381e17b2dd7bcc137bf4d842a779e8d62c63957a978Ji-Hwan Lee                    i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
382d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        } else if (serviceState.mService != null && isStateEmpty) {
3833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is already connected but its state indicates that we have
3843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // nothing to do with it. Then, disconnect the service.
3853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
386d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "unbindService(inputId=" + inputId + ")");
3873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
388d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mContext.unbindService(serviceState.mConnection);
389d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            userState.serviceStateMap.remove(inputId);
3903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
39372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    private ClientState createClientStateLocked(IBinder clientToken, int userId) {
39472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        UserState userState = getUserStateLocked(userId);
39572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState clientState = new ClientState(clientToken, userId);
39672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        try {
39772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            clientToken.linkToDeath(clientState, 0);
39872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        } catch (RemoteException e) {
39972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            Slog.e(TAG, "Client is already died.");
40072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
40172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        userState.clientStateMap.put(clientToken, clientState);
40272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        return clientState;
40372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    }
40472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
4053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
4067de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim            final int userId) {
40772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        final UserState userState = getUserStateLocked(userId);
40872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
4093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (DEBUG) {
410d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
4113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4126a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
4136a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
4146a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
4153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Set up a callback to send the session token.
4163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
4173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
4183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSessionCreated(ITvInputSession session) {
4193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (DEBUG) {
420d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")");
4213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
423d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSession = session;
424fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    if (session == null) {
425fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                        removeSessionStateLocked(sessionToken, userId);
4262b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
4275c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                                null, null, sessionState.mSeq);
428fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    } else {
4292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        try {
4302b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            session.asBinder().linkToDeath(sessionState, 0);
4312b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        } catch (RemoteException e) {
4322b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            Slog.e(TAG, "Session is already died.");
4332b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
43472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
43572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        IBinder clientToken = sessionState.mClient.asBinder();
43672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        ClientState clientState = userState.clientStateMap.get(clientToken);
43772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        if (clientState == null) {
43872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                            clientState = createClientStateLocked(clientToken, userId);
43972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        }
44072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        clientState.mSessionTokens.add(sessionState.mSessionToken);
44172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
442d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
4435c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                                sessionToken, channels[0], sessionState.mSeq);
444fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    }
4456a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    channels[0].dispose();
4463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
448832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
449832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            @Override
4501f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            public void onChannelRetuned(Uri channelUri) {
451a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                synchronized (mLock) {
452a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (DEBUG) {
4531f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
454a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
455a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (sessionState.mSession == null || sessionState.mClient == null) {
456a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        return;
457a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
458a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    try {
4591f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        // TODO: Consider adding this channel change in the watch log. When we do
4601f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        // that, how we can protect the watch log from malicious tv inputs should
4611f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        // be addressed. e.g. add a field which represents where the channel change
4621f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        // originated from.
4631f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
464a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    } catch (RemoteException e) {
4651f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        Slog.e(TAG, "error in onChannelRetuned");
466a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
467a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                }
468a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            }
469a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang
470a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            @Override
4711f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            public void onTrackInfoChanged(List<TvTrackInfo> tracks) {
472a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                synchronized (mLock) {
473a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (DEBUG) {
4741f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        Slog.d(TAG, "onTrackInfoChanged(" + tracks + ")");
475a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
476a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (sessionState.mSession == null || sessionState.mClient == null) {
477a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        return;
478a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
479a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    try {
4801f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        sessionState.mClient.onTrackInfoChanged(tracks,
481a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                                sessionState.mSeq);
482a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    } catch (RemoteException e) {
4831f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        Slog.e(TAG, "error in onTrackInfoChanged");
484b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang                    }
485b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang                }
486b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang            }
487b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang
488b93ccca6139a7ee2dba5c110e5f8213a2bd231e5Dongwon Kang            @Override
4899b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang            public void onVideoAvailable() {
4909b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                synchronized (mLock) {
4919b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    if (DEBUG) {
4929b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                        Slog.d(TAG, "onVideoAvailable()");
4939b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    }
4949b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    if (sessionState.mSession == null || sessionState.mClient == null) {
4959b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                        return;
4969b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    }
4979b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    try {
4989b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                        sessionState.mClient.onVideoAvailable(sessionState.mSeq);
4999b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    } catch (RemoteException e) {
5009b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                        Slog.e(TAG, "error in onVideoAvailable");
5019b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    }
5029b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                }
5039b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang            }
5049b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang
5059b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang            @Override
5069b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang            public void onVideoUnavailable(int reason) {
5079b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                synchronized (mLock) {
5089b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    if (DEBUG) {
5099b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                        Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
5109b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    }
5119b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    if (sessionState.mSession == null || sessionState.mClient == null) {
5129b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                        return;
5139b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    }
5149b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    try {
5159b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                        sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq);
5169b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    } catch (RemoteException e) {
5179b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                        Slog.e(TAG, "error in onVideoUnavailable");
5189b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                    }
5199b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang                }
5209b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang            }
5219b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang
5229b08edff236fc68d836eccfaa1a5f028dc390cecDongwon Kang            @Override
523832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            public void onSessionEvent(String eventType, Bundle eventArgs) {
524832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                synchronized (mLock) {
525832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (DEBUG) {
526832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
527832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
528832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (sessionState.mSession == null || sessionState.mClient == null) {
529832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        return;
530832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
531832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    try {
532832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        sessionState.mClient.onSessionEvent(eventType, eventArgs,
533832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                                sessionState.mSeq);
534832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    } catch (RemoteException e) {
535832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.e(TAG, "error in onSessionEvent");
536832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
537832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                }
538832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            }
5393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
5403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Create a session. When failed, send a null token immediately.
5423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
5436a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            service.createSession(channels[1], callback);
5443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException e) {
5459a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in createSession", e);
546fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang            removeSessionStateLocked(sessionToken, userId);
547d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
5485c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                    sessionState.mSeq);
5493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5506a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        channels[1].dispose();
5513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
553d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
5545c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo            IBinder sessionToken, InputChannel channel, int seq) {
5553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
556d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            client.onSessionCreated(inputId, sessionToken, channel, seq);
5573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException exception) {
5589a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in onSessionCreated", exception);
5593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5602b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
5613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
5632b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
5642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (sessionState.mSession != null) {
5652b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
5662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                sessionState.mSession.release();
5672b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
5682b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.w(TAG, "session is already disapeared", e);
5692b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
5702b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            sessionState.mSession = null;
5713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5722b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        removeSessionStateLocked(sessionToken, userId);
5733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
5743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
575fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
576fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        // Remove the session state from the global session state map of the current user.
577fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        UserState userState = getUserStateLocked(userId);
578fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
579fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
58031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        // Close the open log entry, if any.
581d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (sessionState.mLogUri != null) {
58231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            SomeArgs args = SomeArgs.obtain();
583d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            args.arg1 = sessionState.mLogUri;
58431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            args.arg2 = System.currentTimeMillis();
58531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
58631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
58731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
58872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // Also remove the session token from the session token list of the current client and
58972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // service.
59072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
59172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (clientState != null) {
59272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            clientState.mSessionTokens.remove(sessionToken);
59372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            if (clientState.isEmpty()) {
59472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                userState.clientStateMap.remove(sessionState.mClient.asBinder());
59572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
59672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
59772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
598d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
599fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        if (serviceState != null) {
600d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            serviceState.mSessionTokens.remove(sessionToken);
601fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        }
602d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        updateServiceConnectionLocked(sessionState.mInputId, userId);
603fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    }
604fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
605969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private void unregisterClientInternalLocked(IBinder clientToken, String inputId,
60672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            int userId) {
60772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        UserState userState = getUserStateLocked(userId);
60872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState clientState = userState.clientStateMap.get(clientToken);
60972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (clientState != null) {
61072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            clientState.mInputIds.remove(inputId);
61172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            if (clientState.isEmpty()) {
61272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                userState.clientStateMap.remove(clientToken);
61372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
61472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
61572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
61672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
61772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (serviceState == null) {
61872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            return;
61972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
62072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
62172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // Remove this client from the client list and unregister the callback.
62272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        serviceState.mClientTokens.remove(clientToken);
62372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (!serviceState.mClientTokens.isEmpty()) {
62472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            // We have other clients who want to keep the callback. Do this later.
62572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            return;
62672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
62772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (serviceState.mService == null || serviceState.mCallback == null) {
62872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            return;
62972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
63072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        try {
63172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            serviceState.mService.unregisterCallback(serviceState.mCallback);
63272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        } catch (RemoteException e) {
63372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            Slog.e(TAG, "error in unregisterCallback", e);
63472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        } finally {
63572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            serviceState.mCallback = null;
63672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            updateServiceConnectionLocked(inputId, userId);
63772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
63872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    }
63972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
640969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private void notifyStateChangedLocked(UserState userState, String inputId,
641969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            int state, ITvInputManagerCallback targetCallback) {
642969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (DEBUG) {
643969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            Slog.d(TAG, "notifyStateChangedLocked: inputId = " + inputId
644969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    + "; state = " + state);
645969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
646969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (targetCallback == null) {
647969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (ITvInputManagerCallback callback : userState.callbackSet) {
648969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                try {
649969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    callback.onInputStateChanged(inputId, state);
650969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                } catch (RemoteException e) {
651969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    Slog.e(TAG, "Failed to report state change to callback.");
652969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
653969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
654969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        } else {
6552b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
656969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                targetCallback.onInputStateChanged(inputId, state);
6572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
658969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.e(TAG, "Failed to report state change to callback.");
6592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
6602b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
6612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
6622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
663969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private void setStateLocked(String inputId, int state, int userId) {
664969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        UserState userState = getUserStateLocked(userId);
665969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        TvInputState inputState = userState.inputMap.get(inputId);
666969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
667969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        int oldState = inputState.mState;
668969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        inputState.mState = state;
669969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
670969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                && serviceState.mSessionTokens.isEmpty();
671969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (serviceState != null && serviceState.mService == null && !isStateEmpty) {
672969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            // We don't notify state change while reconnecting. It should remain disconnected.
673969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return;
674969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
675969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (oldState != state) {
676969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            notifyStateChangedLocked(userState, inputId, state, null);
677969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
678969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
679969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
6803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class BinderService extends ITvInputManager.Stub {
6813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public List<TvInputInfo> getTvInputList(int userId) {
6833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
6843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getTvInputList");
6853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
689969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
690969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (TvInputState state : userState.inputMap.values()) {
691969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        inputList.add(state.mInfo);
6923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
693969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    return inputList;
6943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
701969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void registerCallback(final ITvInputManagerCallback callback, int userId) {
7023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
7033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "registerCallback");
7043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
708969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    userState.callbackSet.add(callback);
709969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (TvInputState state : userState.inputMap.values()) {
710969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        notifyStateChangedLocked(userState, state.mInfo.getId(),
711969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                                state.mState, callback);
7123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
720969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
7213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
7223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "unregisterCallback");
7233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
726969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    UserState userState = getUserStateLocked(resolvedUserId);
727969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    userState.callbackSet.remove(callback);
7283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
735d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void createSession(final ITvInputClient client, final String inputId,
7363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int seq, int userId) {
7373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
7383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "createSession");
7403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
744d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
7453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
746d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
747969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                                userState.inputMap.get(inputId).mInfo, resolvedUserId);
748d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
7493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7502b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send a null token immediately while reconnecting.
7512b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mReconnecting == true) {
7525c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
7532b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        return;
7542b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
7552b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
7562b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Create a new session token and a session state.
7572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    IBinder sessionToken = new Binder();
75872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    SessionState sessionState = new SessionState(sessionToken, inputId, client,
75972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                            seq, callingUid, resolvedUserId);
7602b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
7612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Add them to the global session state map of the current user.
7622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    userState.sessionStateMap.put(sessionToken, sessionState);
7632b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
7642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Also, add them to the session state map of the current service.
765d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mSessionTokens.add(sessionToken);
7663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
767d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
768d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        createSessionInternalLocked(serviceState.mService, sessionToken,
7697de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                                resolvedUserId);
7703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
771d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
7723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void releaseSession(IBinder sessionToken, int userId) {
7813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
7823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "releaseSession");
7843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7872b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
7883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
7963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
7973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setSurface");
7993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
8003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
8013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
8023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
8033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
8043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                surface);
8053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
8069a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setSurface", e);
8073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
8083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
810f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                if (surface != null) {
811f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    // surface is not used in TvInputManagerService.
812f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    surface.release();
813f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                }
8143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
8153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
819e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
820e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                int height, int userId) {
821e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final int callingUid = Binder.getCallingUid();
822e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
823e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    userId, "dispatchSurfaceChanged");
824e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            final long identity = Binder.clearCallingIdentity();
825e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            try {
826e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                synchronized (mLock) {
827e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    try {
828e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
829e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                                .dispatchSurfaceChanged(format, width, height);
830e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    } catch (RemoteException e) {
831e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                        Slog.e(TAG, "error in dispatchSurfaceChanged", e);
832e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                    }
833e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                }
834e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            } finally {
835e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho                Binder.restoreCallingIdentity(identity);
836e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho            }
837e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        }
838e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho
839e821d711db1799dc51661a3ed6188f3cd942bae7Youngsang Cho        @Override
8403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setVolume(IBinder sessionToken, float volume, int userId) {
8413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
8423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
8433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setVolume");
8443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
8453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
8463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
8473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
8483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
8493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                volume);
8503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
8519a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setVolume", e);
8523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
8533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
8553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
8563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
8603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void tune(IBinder sessionToken, final Uri channelUri, int userId) {
8613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
8623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
8633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "tune");
8643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
8653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
8663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
8673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
8683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
86931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
87031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long currentTime = System.currentTimeMillis();
87131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long channelId = ContentUris.parseId(channelUri);
87231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
87331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Close the open log entry first, if any.
87431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        UserState userState = getUserStateLocked(resolvedUserId);
87531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
876d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (sessionState.mLogUri != null) {
87731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            SomeArgs args = SomeArgs.obtain();
878d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            args.arg1 = sessionState.mLogUri;
87931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            args.arg2 = currentTime;
88031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
88131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                    .sendToTarget();
88231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        }
88331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
88431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Create a log entry and fill it later.
885969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        String packageName = userState.inputMap.get(sessionState.mInputId).mInfo
8865c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                                .getServiceInfo().packageName;
88731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        ContentValues values = new ContentValues();
8885c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
889f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
89031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                currentTime);
891f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0);
892f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
89331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
894d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sessionState.mLogUri = mContentResolver.insert(
89531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                TvContract.WatchedPrograms.CONTENT_URI, values);
89631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SomeArgs args = SomeArgs.obtain();
897d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        args.arg1 = sessionState.mLogUri;
89831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg2 = ContentUris.parseId(channelUri);
89931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg3 = currentTime;
90031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
9013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
9029a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in tune", e);
9033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
9043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
9053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
9063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
9073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
9083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9109a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
9119a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
9122c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
9132c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final int callingUid = Binder.getCallingUid();
9142c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
9152c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    userId, "setCaptionEnabled");
9162c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            final long identity = Binder.clearCallingIdentity();
9172c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            try {
9182c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                synchronized (mLock) {
9192c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    try {
9202c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
9212c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                                .setCaptionEnabled(enabled);
9222c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    } catch (RemoteException e) {
9232c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                        Slog.e(TAG, "error in setCaptionEnabled", e);
9242c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                    }
9252c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                }
9262c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            } finally {
9272c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo                Binder.restoreCallingIdentity(identity);
9282c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo            }
9292c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        }
9302c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo
9312c1c31c7ae9bd972b974a5cc2d8b0942746af612Jae Seo        @Override
9321f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        public void selectTrack(IBinder sessionToken, TvTrackInfo track, int userId) {
9331f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final int callingUid = Binder.getCallingUid();
9341f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
9351f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    userId, "selectTrack");
9361f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final long identity = Binder.clearCallingIdentity();
9371f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            try {
9381f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                synchronized (mLock) {
9391f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    try {
9401f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
9411f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                                track);
9421f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    } catch (RemoteException e) {
9431f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        Slog.e(TAG, "error in selectTrack", e);
9441f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    }
9451f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                }
9461f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            } finally {
9471f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                Binder.restoreCallingIdentity(identity);
9481f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            }
9491f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        }
9501f213914c45c23c653f721690da2ce0718e63139Dongwon Kang
9511f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        @Override
9521f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        public void unselectTrack(IBinder sessionToken, TvTrackInfo track, int userId) {
9531f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final int callingUid = Binder.getCallingUid();
9541f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
9551f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    userId, "unselectTrack");
9561f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            final long identity = Binder.clearCallingIdentity();
9571f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            try {
9581f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                synchronized (mLock) {
9591f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    try {
9601f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        getSessionLocked(sessionToken, callingUid, resolvedUserId).unselectTrack(
9611f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                                track);
9621f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    } catch (RemoteException e) {
9631f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                        Slog.e(TAG, "error in unselectTrack", e);
9641f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                    }
9651f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                }
9661f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            } finally {
9671f213914c45c23c653f721690da2ce0718e63139Dongwon Kang                Binder.restoreCallingIdentity(identity);
9681f213914c45c23c653f721690da2ce0718e63139Dongwon Kang            }
9691f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        }
9701f213914c45c23c653f721690da2ce0718e63139Dongwon Kang
9711f213914c45c23c653f721690da2ce0718e63139Dongwon Kang        @Override
9729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
9739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                int userId) {
9749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
9759a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
9769a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "createOverlayView");
9779a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
9789a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
9799a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
9809a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
9819a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
9829a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .createOverlayView(windowToken, frame);
9839a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
9849a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in createOverlayView", e);
9859a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
9869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
9879a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
9889a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
9899a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
9909a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
9919a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
9929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
9939a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
9949a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
9959a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
9969a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "relayoutOverlayView");
9979a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
9989a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
9999a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
10009a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
10019a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
10029a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .relayoutOverlayView(frame);
10039a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
10049a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in relayoutOverlayView", e);
10059a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
10069a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
10079a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
10089a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
10099a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
10109a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
10119a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
10129a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
10139a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void removeOverlayView(IBinder sessionToken, int userId) {
10149a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
10159a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
10169a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "removeOverlayView");
10179a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
10189a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
10199a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
10209a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
10219a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
10229a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .removeOverlayView();
10239a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
10249a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in removeOverlayView", e);
10259a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
10269a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
10279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
10289a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
10299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
10309a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
1031c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1032c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1033c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
1034969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1035c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1036c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
1037c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1038c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1039c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1040c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1041c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.getHardwareList();
1042c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1043c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1044c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1045c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1046c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1047c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1048969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void registerTvInputInfo(TvInputInfo info, int deviceId) {
1049969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1050969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1051969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                return;
1052969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
1053969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1054969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1055969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            try {
1056969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                mTvInputHardwareManager.registerTvInputInfo(info, deviceId);
1057969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            } finally {
1058969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Binder.restoreCallingIdentity(identity);
1059969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
1060969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
1061969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1062969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        @Override
1063c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware acquireTvInputHardware(int deviceId,
1064969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                ITvInputHardwareCallback callback, TvInputInfo info, int userId)
1065969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                throws RemoteException {
1066969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1067c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1068c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
1069c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1070c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1071c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1072c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
1073c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1074c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "acquireTvInputHardware");
1075c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1076c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.acquireHardware(
1077969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        deviceId, callback, info, callingUid, resolvedUserId);
1078c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1079c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1080c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1081c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1082c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1083c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1084c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
1085c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
1086969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1087c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
1088c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
1089c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1090c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1091c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
1092c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
1093c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1094c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "releaseTvInputHardware");
1095c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
1096c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mTvInputHardwareManager.releaseHardware(
1097c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, hardware, callingUid, resolvedUserId);
1098c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
1099c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
1100c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1101c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1102e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1103e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        @Override
11040f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo        @SuppressWarnings("resource")
1105e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1106e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
11070f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1108e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    != PackageManager.PERMISSION_GRANTED) {
11090f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo                pw.println("Permission Denial: can't dump TvInputManager from pid="
11100f8fc345ea61928265fdd6d461bf1babe353fbe4Jae Seo                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
1111e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                return;
1112e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            }
1113e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1114e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            synchronized (mLock) {
1115e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.println("User Ids (Current user: " + mCurrentUserId + "):");
1116e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.increaseIndent();
1117e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                for (int i = 0; i < mUserStates.size(); i++) {
1118e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    int userId = mUserStates.keyAt(i);
1119e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println(Integer.valueOf(userId));
1120e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                }
1121e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                pw.decreaseIndent();
1122e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1123e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                for (int i = 0; i < mUserStates.size(); i++) {
1124e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    int userId = mUserStates.keyAt(i);
1125e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    UserState userState = getUserStateLocked(userId);
1126e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("UserState (" + userId + "):");
1127e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1128e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1129969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("inputMap: inputId -> TvInputState");
1130e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1131969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (TvInputState state : userState.inputMap.values()) {
1132969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        pw.println(state.toString());
1133e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1134e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1135e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1136969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("packageSet:");
1137e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1138969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (String packageName : userState.packageSet) {
1139e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(packageName);
1140e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1141e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1142e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1143e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("clientStateMap: ITvInputClient -> ClientState");
1144e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1145e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    for (Map.Entry<IBinder, ClientState> entry :
1146e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.clientStateMap.entrySet()) {
1147e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        ClientState client = entry.getValue();
1148e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + client);
1149e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1150e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1151e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1152e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mInputIds:");
1153e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1154e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        for (String inputId : client.mInputIds) {
1155e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            pw.println(inputId);
1156e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        }
1157e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1158e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1159e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mSessionTokens:");
1160e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1161e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        for (IBinder token : client.mSessionTokens) {
1162e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            pw.println("" + token);
1163e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        }
1164e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1165e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1166e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mClientTokens: " + client.mClientToken);
1167e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mUserId: " + client.mUserId);
1168e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1169e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1170e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1171e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1172e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1173e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("serviceStateMap: inputId -> ServiceState");
1174e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1175e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    for (Map.Entry<String, ServiceState> entry :
1176e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.serviceStateMap.entrySet()) {
1177e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        ServiceState service = entry.getValue();
1178e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + service);
1179e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1180e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1181e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1182e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mClientTokens:");
1183e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1184e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        for (IBinder token : service.mClientTokens) {
1185e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            pw.println("" + token);
1186e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        }
1187e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1188e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1189e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mSessionTokens:");
1190e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1191e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        for (IBinder token : service.mSessionTokens) {
1192e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            pw.println("" + token);
1193e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        }
1194e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1195e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1196e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mService: " + service.mService);
1197e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mCallback: " + service.mCallback);
1198e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mBound: " + service.mBound);
1199e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mReconnecting: " + service.mReconnecting);
1200e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1201e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1202e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1203e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1204e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1205e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.println("sessionStateMap: ITvInputSession -> SessionState");
1206e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.increaseIndent();
1207e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    for (Map.Entry<IBinder, SessionState> entry :
1208e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                            userState.sessionStateMap.entrySet()) {
1209e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        SessionState session = entry.getValue();
1210e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println(entry.getKey() + ": " + session);
1211e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1212e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.increaseIndent();
1213e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mInputId: " + session.mInputId);
1214e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mClient: " + session.mClient);
1215e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mSeq: " + session.mSeq);
1216e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mCallingUid: " + session.mCallingUid);
1217e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mUserId: " + session.mUserId);
1218e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mSessionToken: " + session.mSessionToken);
1219e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mSession: " + session.mSession);
1220e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.println("mLogUri: " + session.mLogUri);
1221e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                        pw.decreaseIndent();
1222e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    }
1223e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1224e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim
1225969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.println("callbackSet:");
1226969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.increaseIndent();
1227969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    for (ITvInputManagerCallback callback : userState.callbackSet) {
1228969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        pw.println(callback.toString());
1229969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    }
1230969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    pw.decreaseIndent();
1231969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1232e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                    pw.decreaseIndent();
1233e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim                }
1234e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim            }
1235e14c3f4fc42e2dc83cf4aba711c5ff52d8bbe3eaJaewan Kim        }
12363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
12373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1238969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private static final class TvInputState {
1239969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A TvInputInfo object which represents the TV input.
1240969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private TvInputInfo mInfo;
1241969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1242969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // The state of TV input. Connected by default.
1243969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private int mState = INPUT_STATE_CONNECTED;
1244969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1245969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        @Override
1246969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public String toString() {
1247969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return "mInfo: " + mInfo + "; mState: " + mState;
1248969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
1249969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
1250969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
12513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class UserState {
1252969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A mapping from the TV input id to its TvInputState.
1253969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
12543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1255969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A set of all TV input packages.
1256969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final Set<String> packageSet = new HashSet<String>();
12575c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo
125872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // A mapping from the token of a client to its state.
125972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final Map<IBinder, ClientState> clientStateMap =
126072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                new HashMap<IBinder, ClientState>();
126172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
12623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the name of a TV input service to its state.
1263d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, ServiceState> serviceStateMap =
1264d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                new HashMap<String, ServiceState>();
12653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
12663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the token of a TV input session to its state.
12673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final Map<IBinder, SessionState> sessionStateMap =
12683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new HashMap<IBinder, SessionState>();
1269969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1270969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        // A set of callbacks.
1271969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final Set<ITvInputManagerCallback> callbackSet =
1272969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                new HashSet<ITvInputManagerCallback>();
12733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
12743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
127572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    private final class ClientState implements IBinder.DeathRecipient {
127672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final List<String> mInputIds = new ArrayList<String>();
127772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
127872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
127972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private IBinder mClientToken;
128072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final int mUserId;
128172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
128272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState(IBinder clientToken, int userId) {
128372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            mClientToken = clientToken;
128472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            mUserId = userId;
128572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
128672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
128772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        public boolean isEmpty() {
128872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            return mInputIds.isEmpty() && mSessionTokens.isEmpty();
128972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
129072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
129172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        @Override
129272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        public void binderDied() {
129372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            synchronized (mLock) {
129472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                UserState userState = getUserStateLocked(mUserId);
129572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                // DO NOT remove the client state of clientStateMap in this method. It will be
1296969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                // removed in releaseSessionLocked() or unregisterClientInternalLocked().
129772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                ClientState clientState = userState.clientStateMap.get(mClientToken);
129872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                if (clientState != null) {
129972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    while (clientState.mSessionTokens.size() > 0) {
130072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        releaseSessionLocked(
130172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                                clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
130272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    }
130372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    while (clientState.mInputIds.size() > 0) {
1304969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        unregisterClientInternalLocked(
130572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                                mClientToken, clientState.mInputIds.get(0), mUserId);
130672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    }
130772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                }
130872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                mClientToken = null;
130972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
131072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
131172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    }
131272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
13133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceState {
131472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final List<IBinder> mClientTokens = new ArrayList<IBinder>();
1315d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
1316d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ServiceConnection mConnection;
13172b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final TvInputInfo mTvInputInfo;
13183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1319d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputService mService;
1320d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceCallback mCallback;
1321d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mBound;
13222b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private boolean mReconnecting;
13233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1324d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceState(TvInputInfo inputInfo, int userId) {
13252b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mTvInputInfo = inputInfo;
13262b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mConnection = new InputServiceConnection(inputInfo, userId);
13273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
13283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
13293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
13302b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private final class SessionState implements IBinder.DeathRecipient {
1331d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final String mInputId;
1332d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ITvInputClient mClient;
1333d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mSeq;
1334d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mCallingUid;
13352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final int mUserId;
133672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final IBinder mSessionToken;
1337d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputSession mSession;
1338d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private Uri mLogUri;
13393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
134072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private SessionState(IBinder sessionToken, String inputId, ITvInputClient client, int seq,
13412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                int callingUid, int userId) {
134272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            mSessionToken = sessionToken;
13432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mInputId = inputId;
13442b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mClient = client;
13452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mSeq = seq;
13462b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mCallingUid = callingUid;
13472b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mUserId = userId;
13482b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
13492b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
13502b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        @Override
13512b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void binderDied() {
13522b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
13532b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                mSession = null;
13542b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (mClient != null) {
13552b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    try {
13562b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        mClient.onSessionReleased(mSeq);
13572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    } catch(RemoteException e) {
13582b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        Slog.e(TAG, "error in onSessionReleased", e);
13592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
13602b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
136172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                removeSessionStateLocked(mSessionToken, mUserId);
13622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
13633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
13643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
13653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
13663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class InputServiceConnection implements ServiceConnection {
1367d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final TvInputInfo mTvInputInfo;
13683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
13693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1370d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private InputServiceConnection(TvInputInfo inputInfo, int userId) {
13713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
1372d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mTvInputInfo = inputInfo;
13733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
13743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
13753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
13763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceConnected(ComponentName name, IBinder service) {
1377969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            String inputId = mTvInputInfo.getId();
13783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
1379969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.d(TAG, "onServiceConnected(inputId=" + inputId + ")");
13803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
13813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
1382969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                UserState userState = getUserStateLocked(mUserId);
1383969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                ServiceState serviceState = userState.serviceStateMap.get(inputId);
1384d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                serviceState.mService = ITvInputService.Stub.asInterface(service);
13853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
13863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // Register a callback, if we need to.
138772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) {
1388969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    serviceState.mCallback = new ServiceCallback(mTvInputInfo.getId(), mUserId);
13893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1390d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.registerCallback(serviceState.mCallback);
13913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
13929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in registerCallback", e);
13933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
13943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
13953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
13963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // And create sessions, if any.
1397d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                for (IBinder sessionToken : serviceState.mSessionTokens) {
1398d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
13993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1400969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1401969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputState inputState = userState.inputMap.get(inputId);
1402969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                if (inputState != null && inputState.mState != INPUT_STATE_DISCONNECTED) {
1403969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    notifyStateChangedLocked(userState, mTvInputInfo.getId(),
1404969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            inputState.mState, null);
1405969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
14063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
14073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
14083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
14093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
14103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceDisconnected(ComponentName name) {
14113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
1412d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
14133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
14142b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!mTvInputInfo.getComponent().equals(name)) {
14152b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                throw new IllegalArgumentException("Mismatched ComponentName: "
14162b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
14172b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
14182b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
14192b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                UserState userState = getUserStateLocked(mUserId);
14202b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
14212b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState != null) {
14222b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mReconnecting = true;
14232b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mBound = false;
14242b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mService = null;
14252b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mCallback = null;
14262b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
14272b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send null tokens for not finishing create session events.
14282b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    for (IBinder sessionToken : serviceState.mSessionTokens) {
14292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
14302b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        if (sessionState.mSession == null) {
14312b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            removeSessionStateLocked(sessionToken, sessionState.mUserId);
14322b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            sendSessionTokenToClientLocked(sessionState.mClient,
14335c80ad2077f3e755413ea47a35f51e9d25dbb083Jae Seo                                    sessionState.mInputId, null, null, sessionState.mSeq);
14342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
14352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
14362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
1437969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    notifyStateChangedLocked(userState, mTvInputInfo.getId(),
1438969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            INPUT_STATE_DISCONNECTED, null);
14392b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
14402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
14412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
14423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
14433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
14443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
14453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
1446969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final String mInputId;
14473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
14483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1449969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        ServiceCallback(String inputId, int userId) {
1450969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mInputId = inputId;
14513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
14523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
14533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
14543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
1455969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void onInputStateChanged(int state) {
14563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
1457969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.d(TAG, "onInputStateChanged(inputId=" + mInputId + ", state="
1458969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        + state + ")");
14593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
14603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
1461969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                setStateLocked(mInputId, state, mUserId);
14623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
14633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
14643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
146531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
146631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final class LogHandler extends Handler {
146731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_OPEN_ENTRY = 1;
146831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_UPDATE_ENTRY = 2;
146931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_CLOSE_ENTRY = 3;
147031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
147131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public LogHandler(Looper looper) {
147231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            super(looper);
147331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
147431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
147531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        @Override
147631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public void handleMessage(Message msg) {
147731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            switch (msg.what) {
147831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_OPEN_ENTRY: {
147931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
148031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
148131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
148231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
148331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onOpenEntry(uri, channelId, time);
148431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
148531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
148631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
148731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_UPDATE_ENTRY: {
148831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
148931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
149031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
149131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
149231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onUpdateEntry(uri, channelId, time);
149331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
149431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
149531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
149631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_CLOSE_ENTRY: {
149731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
149831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
149931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg2;
150031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onCloseEntry(uri, time);
150131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
150231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
150331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
150431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                default: {
15056a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    Slog.w(TAG, "Unhandled message code: " + msg.what);
150631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
150731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
150831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
150931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
151031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
151131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onOpenEntry(Uri uri, long channelId, long watchStarttime) {
151231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
1513f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_TITLE,
1514f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
1515f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
1516bd23fa0ba1460a8d5194fd7c700030bf9c3f6fcbJae Seo                    TvContract.Programs.COLUMN_SHORT_DESCRIPTION
151731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
1518f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND "
1519f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND "
1520f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS + ">?";
152131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] selectionArgs = {
152231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(channelId),
152331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime),
152431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime)
152531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
1526f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            String sortOrder = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC";
152731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
152831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
152931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection,
153031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        selection, selectionArgs, sortOrder);
153131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
153231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
1533f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_TITLE, cursor.getString(0));
1534f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
1535f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                            cursor.getLong(1));
153631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(2);
1537f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
1538f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, cursor.getString(3));
153931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.update(uri, values, null, null);
154031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
154131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Schedule an update when the current program ends.
154231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = SomeArgs.obtain();
154331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg1 = uri;
154431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg2 = channelId;
154531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg3 = endTime;
154631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args);
154731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    sendMessageDelayed(msg, endTime - System.currentTimeMillis());
154831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
154931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
155031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
155131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
155231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
155331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
155431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
155531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
155631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onUpdateEntry(Uri uri, long channelId, long time) {
155731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
1558f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
1559f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
1560f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_TITLE,
1561f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
1562f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS,
1563f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_DESCRIPTION
156431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
156531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
156631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
156731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(uri, projection, null, null, null);
156831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
156931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchStartTime = cursor.getLong(0);
157031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchEndTime = cursor.getLong(1);
157131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String title = cursor.getString(2);
157231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long startTime = cursor.getLong(3);
157331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(4);
157431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String description = cursor.getString(5);
157531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
157631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Do nothing if the current log entry is already closed.
157731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    if (watchEndTime > 0) {
157831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        return;
157931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    }
158031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
158131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // The current program has just ended. Create a (complete) log entry off the
158231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // current entry.
158331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
1584f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
158531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            watchStartTime);
1586f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, time);
1587f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
1588f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_TITLE, title);
1589f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime);
1590f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
1591f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, description);
159231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
159331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
159431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
159531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
159631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
159731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
159831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
159931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            // Re-open the current log entry with the next program information.
160031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            onOpenEntry(uri, channelId, time);
160131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
160231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
160331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onCloseEntry(Uri uri, long watchEndTime) {
160431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            ContentValues values = new ContentValues();
1605f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, watchEndTime);
160631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mContentResolver.update(uri, values, null, null);
160731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
160831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    }
1609969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1610969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    final class Client {
1611969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void setState(String inputId, int state) {
1612969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            synchronized (mLock) {
1613969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                setStateLocked(inputId, state, mCurrentUserId);
1614969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
1615969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
1616969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
16173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
1618