TvInputManagerService.java revision a3be12a236aef0d9c4ff1274075f1e7899d29153
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
193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.app.ActivityManager;
203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.BroadcastReceiver;
213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.ComponentName;
2231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentResolver;
2331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentUris;
2431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.content.ContentValues;
253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Context;
263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.Intent;
273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.IntentFilter;
283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.ServiceConnection;
293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.PackageManager;
303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.ResolveInfo;
313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.content.pm.ServiceInfo;
3231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.database.Cursor;
339a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.graphics.Rect;
34d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputClient;
35d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
36d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
37d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputManager;
38d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputService;
39d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputServiceCallback;
40d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputSession;
41d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputSessionCallback;
42d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvContract;
43d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputHardwareInfo;
44d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputInfo;
45d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputService;
463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.net.Uri;
473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Binder;
48832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Choimport android.os.Bundle;
4931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Handler;
503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.IBinder;
5131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Looper;
5231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Message;
533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Process;
543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.RemoteException;
553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.UserHandle;
569a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.util.Slog;
573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.util.SparseArray;
586a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputChannel;
593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.view.Surface;
603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.internal.content.PackageMonitor;
6231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.internal.os.SomeArgs;
6331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.server.IoThread;
643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.server.SystemService;
653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
66e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Leeimport org.xmlpull.v1.XmlPullParserException;
67e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee
68e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Leeimport java.io.IOException;
69e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee
703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.ArrayList;
713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.HashMap;
723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.List;
733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.Map;
743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo/** This class provides a system service that manages television inputs. */
763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seopublic final class TvInputManagerService extends SystemService {
773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // STOPSHIP: Turn debugging off.
783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final boolean DEBUG = true;
793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final String TAG = "TvInputManagerService";
803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Context mContext;
82c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHardwareManager mTvInputHardwareManager;
833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final ContentResolver mContentResolver;
8531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A global lock.
873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Object mLock = new Object();
883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // ID of the current user.
903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int mCurrentUserId = UserHandle.USER_OWNER;
913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A map from user id to UserState.
933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final Handler mLogHandler;
9631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public TvInputManagerService(Context context) {
983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        super(context);
9931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext = context;
10131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        mContentResolver = context.getContentResolver();
10231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        mLogHandler = new LogHandler(IoThread.get().getLooper());
10331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
104c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mTvInputHardwareManager = new TvInputHardwareManager(context);
1053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        registerBroadcastReceivers();
10631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(mCurrentUserId, new UserState());
1093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            buildTvInputListLocked(mCurrentUserId);
1103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    @Override
1143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public void onStart() {
1153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
1163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void registerBroadcastReceivers() {
1193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageMonitor monitor = new PackageMonitor() {
1203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSomePackagesChanged() {
1223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
1233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    buildTvInputListLocked(mCurrentUserId);
1243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
1273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        monitor.register(mContext, null, UserHandle.ALL, true);
1283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        IntentFilter intentFilter = new IntentFilter();
1303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
1313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
1323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext.registerReceiverAsUser(new BroadcastReceiver() {
1333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onReceive(Context context, Intent intent) {
1353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                String action = intent.getAction();
1363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
1373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
1393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }, UserHandle.ALL, intentFilter, null, null);
1433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void buildTvInputListLocked(int userId) {
1463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
147d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        userState.inputMap.clear();
1483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1499a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        if (DEBUG) Slog.d(TAG, "buildTvInputList");
1503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageManager pm = mContext.getPackageManager();
1513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        List<ResolveInfo> services = pm.queryIntentServices(
152e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                new Intent(TvInputService.SERVICE_INTERFACE),
153e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
1543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        for (ResolveInfo ri : services) {
1553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            ServiceInfo si = ri.serviceInfo;
1563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
1579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
1583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + android.Manifest.permission.BIND_TV_INPUT);
1593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                continue;
1603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
161e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            try {
162e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
163e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                if (DEBUG) Slog.d(TAG, "add " + info.getId());
164e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                userState.inputMap.put(info.getId(), info);
165e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            } catch (IOException | XmlPullParserException e) {
166e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                Slog.e(TAG, "Can't load TV input " + si.name, e);
167e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            }
1683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void switchUser(int userId) {
1723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (mCurrentUserId == userId) {
1743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
1753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // final int oldUserId = mCurrentUserId;
1773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // TODO: Release services and sessions in the old user state, if needed.
1783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mCurrentUserId = userId;
1793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            UserState userState = mUserStates.get(userId);
1813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (userState == null) {
1823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                userState = new UserState();
1833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(userId, userState);
1853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            buildTvInputListLocked(userId);
1863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void removeUser(int userId) {
1903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
191b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            UserState userState = mUserStates.get(userId);
192b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            if (userState == null) {
193b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo                return;
194b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            }
1953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Release created sessions.
1963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (SessionState state : userState.sessionStateMap.values()) {
197d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (state.mSession != null) {
1983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
199d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        state.mSession.release();
2003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
2019a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in release", e);
2023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.sessionStateMap.clear();
2063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Unregister all callbacks and unbind all services.
2083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (ServiceState serviceState : userState.serviceStateMap.values()) {
209d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (serviceState.mCallback != null) {
2103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
211d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.unregisterCallback(serviceState.mCallback);
2123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
2139a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
2143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
216d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                serviceState.mClients.clear();
217d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                mContext.unbindService(serviceState.mConnection);
2183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.serviceStateMap.clear();
2203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.remove(userId);
2223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private UserState getUserStateLocked(int userId) {
2263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = mUserStates.get(userId);
2273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (userState == null) {
2283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("User state not found for user ID " + userId);
2293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return userState;
2313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
233d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private ServiceState getServiceStateLocked(String inputId, int userId) {
2343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
235d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
2363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
237d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            throw new IllegalStateException("Service state not found for " + inputId + " (userId="
2387de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    + userId + ")");
2393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return serviceState;
2413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
2443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
2453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
2463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (sessionState == null) {
2473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalArgumentException("Session state not found for token " + sessionToken);
2483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Only the application that requested this session or the system can access it.
250d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
2513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new SecurityException("Illegal access to the session with token " + sessionToken
2523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + " from uid " + callingUid);
2533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2542b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        return sessionState;
2552b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
2562b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
2572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
2582b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
259d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ITvInputSession session = sessionState.mSession;
2603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (session == null) {
2613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("Session not yet created for token " + sessionToken);
2623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return session;
2643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
2673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            String methodName) {
2683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
2693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                false, methodName, null);
2703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
272d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void updateServiceConnectionLocked(String inputId, int userId) {
2733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
274d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
2753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
2763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return;
2773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2782b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (serviceState.mReconnecting) {
2792b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!serviceState.mSessionTokens.isEmpty()) {
2802b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                // wait until all the sessions are removed.
2812b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                return;
2822b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
2832b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            serviceState.mReconnecting = false;
2842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
285d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        boolean isStateEmpty = serviceState.mClients.isEmpty()
286d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                && serviceState.mSessionTokens.isEmpty();
287d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
2883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is not yet connected but its state indicates that we
2893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // have pending requests. Then, connect the service.
290d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            if (serviceState.mBound) {
2913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // We have already bound to the service so we don't try to bind again until after we
2923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // unbind later on.
2933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
2943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
296d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId
2973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + ")");
2983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
299d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim
300d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
301d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    userState.inputMap.get(inputId).getComponent());
302d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mContext.bindServiceAsUser(i, serviceState.mConnection, Context.BIND_AUTO_CREATE,
3033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    new UserHandle(userId));
304d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            serviceState.mBound = true;
305d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        } else if (serviceState.mService != null && isStateEmpty) {
3063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is already connected but its state indicates that we have
3073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // nothing to do with it. Then, disconnect the service.
3083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
309d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "unbindService(inputId=" + inputId + ")");
3103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
311d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mContext.unbindService(serviceState.mConnection);
312d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            userState.serviceStateMap.remove(inputId);
3133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
3177de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim            final int userId) {
3187de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        final SessionState sessionState =
3197de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                getUserStateLocked(userId).sessionStateMap.get(sessionToken);
3203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (DEBUG) {
321d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
3223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3236a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
3246a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
3256a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
3263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Set up a callback to send the session token.
3273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
3283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
3293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSessionCreated(ITvInputSession session) {
3303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (DEBUG) {
331d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")");
3323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
334d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSession = session;
335fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    if (session == null) {
336fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                        removeSessionStateLocked(sessionToken, userId);
3372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
3382b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                null, null, sessionState.mSeq, userId);
339fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    } else {
3402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        try {
3412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            session.asBinder().linkToDeath(sessionState, 0);
3422b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        } catch (RemoteException e) {
3432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            Slog.e(TAG, "Session is already died.");
3442b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
345d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
346d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                sessionToken, channels[0], sessionState.mSeq, userId);
347fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    }
3486a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    channels[0].dispose();
3493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
351832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
352832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            @Override
353a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            public void onVideoStreamChanged(int width, int height, boolean interlaced) {
354832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                synchronized (mLock) {
355832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (DEBUG) {
356a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.d(TAG, "onVideoStreamChanged(" + width + ", " + height + ")");
357832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
358832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (sessionState.mSession == null || sessionState.mClient == null) {
359832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        return;
360832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
361832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    try {
362a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        sessionState.mClient.onVideoStreamChanged(width, height, interlaced,
363a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                                sessionState.mSeq);
364832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    } catch (RemoteException e) {
365a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.e(TAG, "error in onVideoStreamChanged");
366a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
367a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                }
368a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            }
369a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang
370a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            @Override
371a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            public void onAudioStreamChanged(int channelCount) {
372a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                synchronized (mLock) {
373a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (DEBUG) {
374a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.d(TAG, "onAudioStreamChanged(" + channelCount + ")");
375a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
376a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (sessionState.mSession == null || sessionState.mClient == null) {
377a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        return;
378a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
379a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    try {
380a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        sessionState.mClient.onAudioStreamChanged(channelCount, sessionState.mSeq);
381a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    } catch (RemoteException e) {
382a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.e(TAG, "error in onAudioStreamChanged");
383a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
384a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                }
385a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            }
386a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang
387a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            @Override
388a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            public void onClosedCaptionStreamChanged(boolean hasClosedCaption) {
389a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                synchronized (mLock) {
390a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (DEBUG) {
391a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.d(TAG, "onClosedCaptionStreamChanged(" + hasClosedCaption + ")");
392a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
393a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (sessionState.mSession == null || sessionState.mClient == null) {
394a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        return;
395a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
396a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    try {
397a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        sessionState.mClient.onClosedCaptionStreamChanged(hasClosedCaption,
398a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                                sessionState.mSeq);
399a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    } catch (RemoteException e) {
400a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.e(TAG, "error in onClosedCaptionStreamChanged");
401832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
402832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                }
403832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            }
404832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
405832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            @Override
406832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            public void onSessionEvent(String eventType, Bundle eventArgs) {
407832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                synchronized (mLock) {
408832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (DEBUG) {
409832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
410832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
411832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (sessionState.mSession == null || sessionState.mClient == null) {
412832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        return;
413832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
414832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    try {
415832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        sessionState.mClient.onSessionEvent(eventType, eventArgs,
416832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                                sessionState.mSeq);
417832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    } catch (RemoteException e) {
418832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.e(TAG, "error in onSessionEvent");
419832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
420832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                }
421832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            }
4223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
4233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Create a session. When failed, send a null token immediately.
4253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
4266a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            service.createSession(channels[1], callback);
4273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException e) {
4289a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in createSession", e);
429fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang            removeSessionStateLocked(sessionToken, userId);
430d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
431d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSeq, userId);
4323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4336a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        channels[1].dispose();
4343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
436d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
4376a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            IBinder sessionToken, InputChannel channel, int seq, int userId) {
4383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
439d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            client.onSessionCreated(inputId, sessionToken, channel, seq);
4403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException exception) {
4419a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in onSessionCreated", exception);
4423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
4443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
4462b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
4472b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (sessionState.mSession != null) {
4482b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
4492b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                sessionState.mSession.release();
4502b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
4512b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.w(TAG, "session is already disapeared", e);
4522b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
4532b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            sessionState.mSession = null;
4543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4552b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        removeSessionStateLocked(sessionToken, userId);
4563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
458fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
459fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        // Remove the session state from the global session state map of the current user.
460fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        UserState userState = getUserStateLocked(userId);
461fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
462fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
46331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        // Close the open log entry, if any.
464d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (sessionState.mLogUri != null) {
46531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            SomeArgs args = SomeArgs.obtain();
466d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            args.arg1 = sessionState.mLogUri;
46731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            args.arg2 = System.currentTimeMillis();
46831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
46931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
47031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
4717de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        // Also remove the session token from the session token list of the current service.
472d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
473fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        if (serviceState != null) {
474d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            serviceState.mSessionTokens.remove(sessionToken);
475fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        }
476d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        updateServiceConnectionLocked(sessionState.mInputId, userId);
477fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    }
478fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
4792b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
4802b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        for (IBinder iBinder : serviceState.mClients) {
4812b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
4822b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
4832b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                client.onAvailabilityChanged(
4842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
4852b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
4862b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.e(TAG, "error in onAvailabilityChanged", e);
4872b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
4882b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
4892b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
4902b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
4913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class BinderService extends ITvInputManager.Stub {
4923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
4933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public List<TvInputInfo> getTvInputList(int userId) {
4943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getTvInputList");
4963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
500d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    return new ArrayList<TvInputInfo>(userState.inputMap.values());
5013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
508d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public boolean getAvailability(final ITvInputClient client, final String inputId,
5093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
5103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
5113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getAvailability");
5123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
516d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
5173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState != null) {
5183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We already know the status of this input service. Return the cached
5193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // status.
520d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        return serviceState.mAvailable;
5213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return false;
5273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
530d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void registerCallback(final ITvInputClient client, final String inputId,
5313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
5323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
5333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "registerCallback");
5343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Create a new service callback and add it to the callback map of the current
5383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // service.
5393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
540d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
5413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
542d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
543d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                userState.inputMap.get(inputId), resolvedUserId);
544d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
5453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    IBinder iBinder = client.asBinder();
547d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (!serviceState.mClients.contains(iBinder)) {
548d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mClients.add(iBinder);
5493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
550d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
551d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (serviceState.mCallback != null) {
5523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            // We already handled.
5533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            return;
5543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
555d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mCallback = new ServiceCallback(resolvedUserId);
5563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        try {
557d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            serviceState.mService.registerCallback(serviceState.mCallback);
5583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        } catch (RemoteException e) {
5599a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                            Slog.e(TAG, "error in registerCallback", e);
5603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
5613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
562d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
5633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
571d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void unregisterCallback(ITvInputClient client, String inputId, int userId) {
5723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
5733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "unregisterCallback");
5743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
578d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
5793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
5803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
5813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Remove this client from the client list and unregister the callback.
584d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mClients.remove(client.asBinder());
585d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (!serviceState.mClients.isEmpty()) {
5863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We have other clients who want to keep the callback. Do this later.
5873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
5883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
589d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService == null || serviceState.mCallback == null) {
5903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
5913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
593d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.unregisterCallback(serviceState.mCallback);
5943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
5959a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
5963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } finally {
597d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mCallback = null;
598d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
5993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
607d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void createSession(final ITvInputClient client, final String inputId,
6083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int seq, int userId) {
6093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "createSession");
6123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
616d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
6173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
618d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
619d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                userState.inputMap.get(inputId), resolvedUserId);
620d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
6213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6222b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send a null token immediately while reconnecting.
6232b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mReconnecting == true) {
6242b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
6252b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        return;
6262b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
6272b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
6282b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Create a new session token and a session state.
6292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    IBinder sessionToken = new Binder();
6302b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    SessionState sessionState = new SessionState(
6312b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            sessionToken, inputId, client, seq, callingUid, resolvedUserId);
6322b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
6332b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Add them to the global session state map of the current user.
6342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    userState.sessionStateMap.put(sessionToken, sessionState);
6352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
6362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Also, add them to the session state map of the current service.
637d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mSessionTokens.add(sessionToken);
6383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
639d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
640d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        createSessionInternalLocked(serviceState.mService, sessionToken,
6417de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                                resolvedUserId);
6423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
643d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
6443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void releaseSession(IBinder sessionToken, int userId) {
6533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "releaseSession");
6563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
6603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
6683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setSurface");
6713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
6753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
6763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                surface);
6773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
6789a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setSurface", e);
6793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
682f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                if (surface != null) {
683f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    // surface is not used in TvInputManagerService.
684f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    surface.release();
685f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                }
6863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setVolume(IBinder sessionToken, float volume, int userId) {
6923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setVolume");
6953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
6993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
7003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                volume);
7013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
7029a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setVolume", e);
7033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void tune(IBinder sessionToken, final Uri channelUri, int userId) {
7123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
7133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "tune");
7153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
7193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
72031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
72131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long currentTime = System.currentTimeMillis();
72231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long channelId = ContentUris.parseId(channelUri);
72331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
72431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Close the open log entry first, if any.
72531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        UserState userState = getUserStateLocked(resolvedUserId);
72631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
727d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (sessionState.mLogUri != null) {
72831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            SomeArgs args = SomeArgs.obtain();
729d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            args.arg1 = sessionState.mLogUri;
73031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            args.arg2 = currentTime;
73131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
73231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                    .sendToTarget();
73331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        }
73431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
73531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Create a log entry and fill it later.
73631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        ContentValues values = new ContentValues();
737f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
73831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                currentTime);
739f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0);
740f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
74131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
742d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sessionState.mLogUri = mContentResolver.insert(
74331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                TvContract.WatchedPrograms.CONTENT_URI, values);
74431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SomeArgs args = SomeArgs.obtain();
745d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        args.arg1 = sessionState.mLogUri;
74631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg2 = ContentUris.parseId(channelUri);
74731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg3 = currentTime;
74831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
7493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
7509a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in tune", e);
7513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
7523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7589a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
7599a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
7609a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
7619a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                int userId) {
7629a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
7639a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7649a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "createOverlayView");
7659a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
7669a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
7679a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
7689a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
7699a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
7709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .createOverlayView(windowToken, frame);
7719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
7729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in createOverlayView", e);
7739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
7749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
7759a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
7769a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
7779a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
7789a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
7799a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
7809a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
7819a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
7829a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
7839a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7849a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "relayoutOverlayView");
7859a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
7869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
7879a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
7889a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
7899a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
7909a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .relayoutOverlayView(frame);
7919a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
7929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in relayoutOverlayView", e);
7939a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
7949a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
7959a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
7969a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
7979a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
7989a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
7999a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
8009a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
8019a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void removeOverlayView(IBinder sessionToken, int userId) {
8029a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
8039a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
8049a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "removeOverlayView");
8059a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
8069a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
8079a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
8089a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
8099a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
8109a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .removeOverlayView();
8119a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
8129a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in removeOverlayView", e);
8139a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
8149a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
8159a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
8169a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
8179a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
8189a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
819c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
820c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
821c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
822c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
823c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
824c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
825c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
826c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
827c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
828c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
829c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
830c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.getHardwareList();
831c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
832c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
833c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
834c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
835c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
836c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
837c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware acquireTvInputHardware(int deviceId,
838c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                ITvInputHardwareCallback callback, int userId) throws RemoteException {
839c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
840c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
841c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
842c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
843c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
844c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
845c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
846c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
847c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
848c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "acquireTvInputHardware");
849c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
850c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.acquireHardware(
851c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, callback, callingUid, resolvedUserId);
852c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
853c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
854c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
855c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
856c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
857c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
858c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
859c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
860c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
861c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
862c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
863c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
864c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
865c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
866c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
867c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
868c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
869c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "releaseTvInputHardware");
870c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
871c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mTvInputHardwareManager.releaseHardware(
872c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, hardware, callingUid, resolvedUserId);
873c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
874c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
875c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
876c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
8773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
8783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class UserState {
880d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        // A mapping from the TV input id to its TvInputInfo.
881d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
8823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the name of a TV input service to its state.
884d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, ServiceState> serviceStateMap =
885d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                new HashMap<String, ServiceState>();
8863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the token of a TV input session to its state.
8883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final Map<IBinder, SessionState> sessionStateMap =
8893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new HashMap<IBinder, SessionState>();
8903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
8913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceState {
8932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        // TODO: need to implement DeathRecipient for clients.
894d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final List<IBinder> mClients = new ArrayList<IBinder>();
895d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
896d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ServiceConnection mConnection;
8972b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final TvInputInfo mTvInputInfo;
8983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
899d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputService mService;
900d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceCallback mCallback;
901d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mBound;
902d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mAvailable;
9032b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private boolean mReconnecting;
9043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
905d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceState(TvInputInfo inputInfo, int userId) {
9062b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mTvInputInfo = inputInfo;
9072b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mConnection = new InputServiceConnection(inputInfo, userId);
9083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
9103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9112b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private final class SessionState implements IBinder.DeathRecipient {
912d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final String mInputId;
913d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ITvInputClient mClient;
914d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mSeq;
915d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mCallingUid;
9162b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final int mUserId;
9172b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final IBinder mToken;
918d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputSession mSession;
919d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private Uri mLogUri;
9203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9212b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
9222b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                int callingUid, int userId) {
9232b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mToken = token;
9242b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mInputId = inputId;
9252b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mClient = client;
9262b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mSeq = seq;
9272b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mCallingUid = callingUid;
9282b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mUserId = userId;
9292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
9302b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
9312b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        @Override
9322b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void binderDied() {
9332b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
9342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                mSession = null;
9352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (mClient != null) {
9362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    try {
9372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        mClient.onSessionReleased(mSeq);
9382b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    } catch(RemoteException e) {
9392b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        Slog.e(TAG, "error in onSessionReleased", e);
9402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
9412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
9422b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                removeSessionStateLocked(mToken, mUserId);
9432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
9443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
9463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class InputServiceConnection implements ServiceConnection {
948d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final TvInputInfo mTvInputInfo;
9493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
9503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
951d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private InputServiceConnection(TvInputInfo inputInfo, int userId) {
9523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
953d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mTvInputInfo = inputInfo;
9543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
9573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceConnected(ComponentName name, IBinder service) {
9583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
959d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")");
9603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
962d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId);
963d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                serviceState.mService = ITvInputService.Stub.asInterface(service);
9643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // Register a callback, if we need to.
966d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) {
967d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mCallback = new ServiceCallback(mUserId);
9683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
969d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.registerCallback(serviceState.mCallback);
9703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
9719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in registerCallback", e);
9723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
9733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
9743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // And create sessions, if any.
976d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                for (IBinder sessionToken : serviceState.mSessionTokens) {
977d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
9783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
9793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
9833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceDisconnected(ComponentName name) {
9843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
985d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
9863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9872b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!mTvInputInfo.getComponent().equals(name)) {
9882b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                throw new IllegalArgumentException("Mismatched ComponentName: "
9892b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
9902b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
9912b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
9922b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                UserState userState = getUserStateLocked(mUserId);
9932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
9942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState != null) {
9952b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mReconnecting = true;
9962b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mBound = false;
9972b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mService = null;
9982b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mCallback = null;
9992b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
10002b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send null tokens for not finishing create session events.
10012b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    for (IBinder sessionToken : serviceState.mSessionTokens) {
10022b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
10032b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        if (sessionState.mSession == null) {
10042b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            removeSessionStateLocked(sessionToken, sessionState.mUserId);
10052b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            sendSessionTokenToClientLocked(sessionState.mClient,
10062b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                    sessionState.mInputId, null, null, sessionState.mSeq,
10072b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                    sessionState.mUserId);
10082b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
10092b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
10102b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
10112b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mAvailable) {
10122b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        serviceState.mAvailable = false;
10132b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        broadcastServiceAvailabilityChangedLocked(serviceState);
10142b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
10152b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
10162b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
10172b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
10183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
10203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
10223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
10233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ServiceCallback(int userId) {
10253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
10263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
10292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void onAvailabilityChanged(String inputId, boolean isAvailable) {
10303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
1031d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
10323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + isAvailable + ")");
10333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
1035d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
10362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState.mAvailable != isAvailable) {
10372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mAvailable = isAvailable;
10382b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    broadcastServiceAvailabilityChangedLocked(serviceState);
10393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
10403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
104331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
104431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final class LogHandler extends Handler {
104531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_OPEN_ENTRY = 1;
104631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_UPDATE_ENTRY = 2;
104731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_CLOSE_ENTRY = 3;
104831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
104931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public LogHandler(Looper looper) {
105031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            super(looper);
105131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
105231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
105331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        @Override
105431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public void handleMessage(Message msg) {
105531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            switch (msg.what) {
105631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_OPEN_ENTRY: {
105731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
105831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
105931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
106031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
106131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onOpenEntry(uri, channelId, time);
106231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
106331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
106431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
106531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_UPDATE_ENTRY: {
106631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
106731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
106831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
106931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
107031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onUpdateEntry(uri, channelId, time);
107131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
107231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
107331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
107431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_CLOSE_ENTRY: {
107531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
107631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
107731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg2;
107831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onCloseEntry(uri, time);
107931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
108031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
108131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
108231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                default: {
10836a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    Slog.w(TAG, "Unhandled message code: " + msg.what);
108431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
108531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
108631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
108731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
108831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
108931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onOpenEntry(Uri uri, long channelId, long watchStarttime) {
109031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
1091f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_TITLE,
1092f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
1093f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
1094f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_DESCRIPTION
109531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
1096f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND "
1097f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND "
1098f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS + ">?";
109931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] selectionArgs = {
110031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(channelId),
110131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime),
110231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime)
110331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
1104f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            String sortOrder = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC";
110531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
110631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
110731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection,
110831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        selection, selectionArgs, sortOrder);
110931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
111031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
1111f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_TITLE, cursor.getString(0));
1112f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
1113f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                            cursor.getLong(1));
111431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(2);
1115f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
1116f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, cursor.getString(3));
111731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.update(uri, values, null, null);
111831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
111931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Schedule an update when the current program ends.
112031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = SomeArgs.obtain();
112131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg1 = uri;
112231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg2 = channelId;
112331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg3 = endTime;
112431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args);
112531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    sendMessageDelayed(msg, endTime - System.currentTimeMillis());
112631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
112731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
112831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
112931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
113031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
113131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
113231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
113331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
113431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onUpdateEntry(Uri uri, long channelId, long time) {
113531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
1136f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
1137f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
1138f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_TITLE,
1139f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
1140f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS,
1141f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_DESCRIPTION
114231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
114331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
114431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
114531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(uri, projection, null, null, null);
114631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
114731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchStartTime = cursor.getLong(0);
114831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchEndTime = cursor.getLong(1);
114931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String title = cursor.getString(2);
115031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long startTime = cursor.getLong(3);
115131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(4);
115231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String description = cursor.getString(5);
115331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
115431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Do nothing if the current log entry is already closed.
115531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    if (watchEndTime > 0) {
115631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        return;
115731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    }
115831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
115931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // The current program has just ended. Create a (complete) log entry off the
116031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // current entry.
116131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
1162f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
116331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            watchStartTime);
1164f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, time);
1165f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
1166f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_TITLE, title);
1167f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime);
1168f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
1169f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, description);
117031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
117131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
117231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
117331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
117431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
117531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
117631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
117731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            // Re-open the current log entry with the next program information.
117831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            onOpenEntry(uri, channelId, time);
117931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
118031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
118131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onCloseEntry(Uri uri, long watchEndTime) {
118231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            ContentValues values = new ContentValues();
1183f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, watchEndTime);
118431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mContentResolver.update(uri, values, null, null);
118531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
118631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    }
11873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
1188