TvInputManagerService.java revision 0ceb7e4755015eafda29c251eac285620788a51b
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);
10531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(mCurrentUserId, new UserState());
1083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    @Override
1123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public void onStart() {
1133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
1143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1160ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    @Override
1170ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    public void onBootPhase(int phase) {
1180ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1190ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            registerBroadcastReceivers();
1200ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            synchronized (mLock) {
1210ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee                buildTvInputListLocked(mCurrentUserId);
1220ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee            }
1230ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee        }
1240ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee    }
1250ceb7e4755015eafda29c251eac285620788a51bJi-Hwan Lee
1263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void registerBroadcastReceivers() {
1273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageMonitor monitor = new PackageMonitor() {
1283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSomePackagesChanged() {
1303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
1313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    buildTvInputListLocked(mCurrentUserId);
1323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
1353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        monitor.register(mContext, null, UserHandle.ALL, true);
1363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        IntentFilter intentFilter = new IntentFilter();
1383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
1393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
1403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext.registerReceiverAsUser(new BroadcastReceiver() {
1413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onReceive(Context context, Intent intent) {
1433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                String action = intent.getAction();
1443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
1453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
1473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }, UserHandle.ALL, intentFilter, null, null);
1513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void buildTvInputListLocked(int userId) {
1543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
155d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        userState.inputMap.clear();
1563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        if (DEBUG) Slog.d(TAG, "buildTvInputList");
1583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageManager pm = mContext.getPackageManager();
1593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        List<ResolveInfo> services = pm.queryIntentServices(
160e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                new Intent(TvInputService.SERVICE_INTERFACE),
161e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
1623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        for (ResolveInfo ri : services) {
1633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            ServiceInfo si = ri.serviceInfo;
1643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
1659a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
1663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + android.Manifest.permission.BIND_TV_INPUT);
1673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                continue;
1683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
169e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            try {
170e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
171e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                if (DEBUG) Slog.d(TAG, "add " + info.getId());
172e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                userState.inputMap.put(info.getId(), info);
173e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            } catch (IOException | XmlPullParserException e) {
174e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee                Slog.e(TAG, "Can't load TV input " + si.name, e);
175e7bb7d6bb2257c24076f5a4b9f536f90a6637f58Chulwoo Lee            }
1763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void switchUser(int userId) {
1803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (mCurrentUserId == userId) {
1823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
1833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // final int oldUserId = mCurrentUserId;
1853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // TODO: Release services and sessions in the old user state, if needed.
1863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mCurrentUserId = userId;
1873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            UserState userState = mUserStates.get(userId);
1893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (userState == null) {
1903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                userState = new UserState();
1913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(userId, userState);
1933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            buildTvInputListLocked(userId);
1943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void removeUser(int userId) {
1983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
199b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            UserState userState = mUserStates.get(userId);
200b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            if (userState == null) {
201b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo                return;
202b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            }
2033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Release created sessions.
2043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (SessionState state : userState.sessionStateMap.values()) {
205d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (state.mSession != null) {
2063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
207d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        state.mSession.release();
2083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
2099a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in release", e);
2103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.sessionStateMap.clear();
2143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Unregister all callbacks and unbind all services.
2163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (ServiceState serviceState : userState.serviceStateMap.values()) {
217d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (serviceState.mCallback != null) {
2183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
219d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.unregisterCallback(serviceState.mCallback);
2203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
2219a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
2223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
22472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                serviceState.mClientTokens.clear();
225d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                mContext.unbindService(serviceState.mConnection);
2263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.serviceStateMap.clear();
2283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
22972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            userState.clientStateMap.clear();
23072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
2313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.remove(userId);
2323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private UserState getUserStateLocked(int userId) {
2363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = mUserStates.get(userId);
2373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (userState == null) {
2383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("User state not found for user ID " + userId);
2393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return userState;
2413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
243d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private ServiceState getServiceStateLocked(String inputId, int userId) {
2443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
245d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
2463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
247d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            throw new IllegalStateException("Service state not found for " + inputId + " (userId="
2487de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    + userId + ")");
2493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return serviceState;
2513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2532b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
2543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
2553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
2563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (sessionState == null) {
2573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalArgumentException("Session state not found for token " + sessionToken);
2583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Only the application that requested this session or the system can access it.
260d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
2613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new SecurityException("Illegal access to the session with token " + sessionToken
2623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + " from uid " + callingUid);
2633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        return sessionState;
2652b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
2662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
2672b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
2682b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
269d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ITvInputSession session = sessionState.mSession;
2703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (session == null) {
2713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("Session not yet created for token " + sessionToken);
2723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return session;
2743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
2773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            String methodName) {
2783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
2793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                false, methodName, null);
2803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
282d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void updateServiceConnectionLocked(String inputId, int userId) {
2833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
284d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
2853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
2863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return;
2873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2882b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (serviceState.mReconnecting) {
2892b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!serviceState.mSessionTokens.isEmpty()) {
2902b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                // wait until all the sessions are removed.
2912b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                return;
2922b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
2932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            serviceState.mReconnecting = false;
2942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
29572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
296d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                && serviceState.mSessionTokens.isEmpty();
297d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
2983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is not yet connected but its state indicates that we
2993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // have pending requests. Then, connect the service.
300d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            if (serviceState.mBound) {
3013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // We have already bound to the service so we don't try to bind again until after we
3023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // unbind later on.
3033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
3043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
3053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
306d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId
3073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + ")");
3083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
309d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim
310d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
311d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    userState.inputMap.get(inputId).getComponent());
312e17b2dd7bcc137bf4d842a779e8d62c63957a978Ji-Hwan Lee            serviceState.mBound = mContext.bindServiceAsUser(
313e17b2dd7bcc137bf4d842a779e8d62c63957a978Ji-Hwan Lee                    i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
314d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        } else if (serviceState.mService != null && isStateEmpty) {
3153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is already connected but its state indicates that we have
3163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // nothing to do with it. Then, disconnect the service.
3173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
318d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "unbindService(inputId=" + inputId + ")");
3193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
320d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mContext.unbindService(serviceState.mConnection);
321d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            userState.serviceStateMap.remove(inputId);
3223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
32572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    private ClientState createClientStateLocked(IBinder clientToken, int userId) {
32672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        UserState userState = getUserStateLocked(userId);
32772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState clientState = new ClientState(clientToken, userId);
32872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        try {
32972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            clientToken.linkToDeath(clientState, 0);
33072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        } catch (RemoteException e) {
33172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            Slog.e(TAG, "Client is already died.");
33272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
33372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        userState.clientStateMap.put(clientToken, clientState);
33472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        return clientState;
33572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    }
33672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
3373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
3387de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim            final int userId) {
33972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        final UserState userState = getUserStateLocked(userId);
34072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
3413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (DEBUG) {
342d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
3433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3446a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
3456a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
3466a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
3473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Set up a callback to send the session token.
3483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
3493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
3503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSessionCreated(ITvInputSession session) {
3513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (DEBUG) {
352d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")");
3533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
355d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSession = session;
356fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    if (session == null) {
357fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                        removeSessionStateLocked(sessionToken, userId);
3582b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
3592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                null, null, sessionState.mSeq, userId);
360fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    } else {
3612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        try {
3622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            session.asBinder().linkToDeath(sessionState, 0);
3632b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        } catch (RemoteException e) {
3642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            Slog.e(TAG, "Session is already died.");
3652b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
36672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
36772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        IBinder clientToken = sessionState.mClient.asBinder();
36872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        ClientState clientState = userState.clientStateMap.get(clientToken);
36972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        if (clientState == null) {
37072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                            clientState = createClientStateLocked(clientToken, userId);
37172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        }
37272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        clientState.mSessionTokens.add(sessionState.mSessionToken);
37372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
374d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
375d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                sessionToken, channels[0], sessionState.mSeq, userId);
376fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    }
3776a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    channels[0].dispose();
3783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
380832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
381832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            @Override
382a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            public void onVideoStreamChanged(int width, int height, boolean interlaced) {
383832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                synchronized (mLock) {
384832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (DEBUG) {
385a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.d(TAG, "onVideoStreamChanged(" + width + ", " + height + ")");
386832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
387832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (sessionState.mSession == null || sessionState.mClient == null) {
388832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        return;
389832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
390832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    try {
391a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        sessionState.mClient.onVideoStreamChanged(width, height, interlaced,
392a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                                sessionState.mSeq);
393832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    } catch (RemoteException e) {
394a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.e(TAG, "error in onVideoStreamChanged");
395a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
396a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                }
397a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            }
398a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang
399a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            @Override
400a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            public void onAudioStreamChanged(int channelCount) {
401a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                synchronized (mLock) {
402a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (DEBUG) {
403a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.d(TAG, "onAudioStreamChanged(" + channelCount + ")");
404a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
405a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (sessionState.mSession == null || sessionState.mClient == null) {
406a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        return;
407a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
408a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    try {
409a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        sessionState.mClient.onAudioStreamChanged(channelCount, sessionState.mSeq);
410a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    } catch (RemoteException e) {
411a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.e(TAG, "error in onAudioStreamChanged");
412a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
413a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                }
414a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            }
415a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang
416a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            @Override
417a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang            public void onClosedCaptionStreamChanged(boolean hasClosedCaption) {
418a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                synchronized (mLock) {
419a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (DEBUG) {
420a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.d(TAG, "onClosedCaptionStreamChanged(" + hasClosedCaption + ")");
421a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
422a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    if (sessionState.mSession == null || sessionState.mClient == null) {
423a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        return;
424a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    }
425a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    try {
426a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        sessionState.mClient.onClosedCaptionStreamChanged(hasClosedCaption,
427a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                                sessionState.mSeq);
428a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                    } catch (RemoteException e) {
429a3be12a236aef0d9c4ff1274075f1e7899d29153Dongwon Kang                        Slog.e(TAG, "error in onClosedCaptionStreamChanged");
430832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
431832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                }
432832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            }
433832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
434832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            @Override
435832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            public void onSessionEvent(String eventType, Bundle eventArgs) {
436832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                synchronized (mLock) {
437832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (DEBUG) {
438832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
439832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
440832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (sessionState.mSession == null || sessionState.mClient == null) {
441832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        return;
442832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
443832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    try {
444832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        sessionState.mClient.onSessionEvent(eventType, eventArgs,
445832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                                sessionState.mSeq);
446832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    } catch (RemoteException e) {
447832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.e(TAG, "error in onSessionEvent");
448832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
449832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                }
450832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            }
4513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
4523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Create a session. When failed, send a null token immediately.
4543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
4556a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            service.createSession(channels[1], callback);
4563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException e) {
4579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in createSession", e);
458fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang            removeSessionStateLocked(sessionToken, userId);
459d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
460d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSeq, userId);
4613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4626a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        channels[1].dispose();
4633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
465d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
4666a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            IBinder sessionToken, InputChannel channel, int seq, int userId) {
4673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
468d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            client.onSessionCreated(inputId, sessionToken, channel, seq);
4693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException exception) {
4709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in onSessionCreated", exception);
4713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4722b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
4733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4742b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
4752b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
4762b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (sessionState.mSession != null) {
4772b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
4782b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                sessionState.mSession.release();
4792b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
4802b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.w(TAG, "session is already disapeared", e);
4812b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
4822b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            sessionState.mSession = null;
4833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        removeSessionStateLocked(sessionToken, userId);
4853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
487fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
488fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        // Remove the session state from the global session state map of the current user.
489fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        UserState userState = getUserStateLocked(userId);
490fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
491fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
49231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        // Close the open log entry, if any.
493d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (sessionState.mLogUri != null) {
49431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            SomeArgs args = SomeArgs.obtain();
495d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            args.arg1 = sessionState.mLogUri;
49631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            args.arg2 = System.currentTimeMillis();
49731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
49831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
49931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
50072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // Also remove the session token from the session token list of the current client and
50172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // service.
50272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
50372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (clientState != null) {
50472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            clientState.mSessionTokens.remove(sessionToken);
50572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            if (clientState.isEmpty()) {
50672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                userState.clientStateMap.remove(sessionState.mClient.asBinder());
50772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
50872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
50972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
510d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
511fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        if (serviceState != null) {
512d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            serviceState.mSessionTokens.remove(sessionToken);
513fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        }
514d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        updateServiceConnectionLocked(sessionState.mInputId, userId);
515fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    }
516fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
51772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    private void unregisterCallbackInternalLocked(IBinder clientToken, String inputId,
51872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            int userId) {
51972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        UserState userState = getUserStateLocked(userId);
52072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState clientState = userState.clientStateMap.get(clientToken);
52172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (clientState != null) {
52272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            clientState.mInputIds.remove(inputId);
52372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            if (clientState.isEmpty()) {
52472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                userState.clientStateMap.remove(clientToken);
52572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
52672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
52772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
52872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
52972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (serviceState == null) {
53072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            return;
53172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
53272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
53372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // Remove this client from the client list and unregister the callback.
53472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        serviceState.mClientTokens.remove(clientToken);
53572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (!serviceState.mClientTokens.isEmpty()) {
53672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            // We have other clients who want to keep the callback. Do this later.
53772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            return;
53872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
53972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        if (serviceState.mService == null || serviceState.mCallback == null) {
54072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            return;
54172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
54272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        try {
54372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            serviceState.mService.unregisterCallback(serviceState.mCallback);
54472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        } catch (RemoteException e) {
54572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            Slog.e(TAG, "error in unregisterCallback", e);
54672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        } finally {
54772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            serviceState.mCallback = null;
54872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            updateServiceConnectionLocked(inputId, userId);
54972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
55072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    }
55172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
5522b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
55372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        for (IBinder clientToken : serviceState.mClientTokens) {
5542b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
55572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged(
5562b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
5572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
5582b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.e(TAG, "error in onAvailabilityChanged", e);
5592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
5602b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
5612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
5622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class BinderService extends ITvInputManager.Stub {
5643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
5653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public List<TvInputInfo> getTvInputList(int userId) {
5663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
5673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getTvInputList");
5683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
572d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    return new ArrayList<TvInputInfo>(userState.inputMap.values());
5733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
580d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public boolean getAvailability(final ITvInputClient client, final String inputId,
5813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
5823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
5833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getAvailability");
5843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
588d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
5893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState != null) {
5903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We already know the status of this input service. Return the cached
5913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // status.
592d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        return serviceState.mAvailable;
5933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return false;
5993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
602d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void registerCallback(final ITvInputClient client, final String inputId,
6033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
6043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
6053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "registerCallback");
6063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Create a new service callback and add it to the callback map of the current
6103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // service.
6113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
612d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
6133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
614d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
615d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                userState.inputMap.get(inputId), resolvedUserId);
616d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
6173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
61872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    IBinder clientToken = client.asBinder();
61972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    if (!serviceState.mClientTokens.contains(clientToken)) {
62072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        serviceState.mClientTokens.add(clientToken);
6213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
62272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
62372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    ClientState clientState = userState.clientStateMap.get(clientToken);
62472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    if (clientState == null) {
62572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        clientState = createClientStateLocked(clientToken, resolvedUserId);
62672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    }
62772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    if (!clientState.mInputIds.contains(inputId)) {
62872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        clientState.mInputIds.add(inputId);
62972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    }
63072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
631d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
632d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (serviceState.mCallback != null) {
6333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            // We already handled.
6343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            return;
6353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
636d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mCallback = new ServiceCallback(resolvedUserId);
6373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        try {
638d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            serviceState.mService.registerCallback(serviceState.mCallback);
6393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        } catch (RemoteException e) {
6409a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                            Slog.e(TAG, "error in registerCallback", e);
6413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
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
652d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void unregisterCallback(ITvInputClient client, String inputId, int userId) {
6533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
6543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "unregisterCallback");
6553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
65872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    unregisterCallbackInternalLocked(client.asBinder(), inputId, resolvedUserId);
6593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
666d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void createSession(final ITvInputClient client, final String inputId,
6673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int seq, int userId) {
6683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "createSession");
6713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
675d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
6763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
677d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
678d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                userState.inputMap.get(inputId), resolvedUserId);
679d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
6803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6812b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send a null token immediately while reconnecting.
6822b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mReconnecting == true) {
6832b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
6842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        return;
6852b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
6862b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
6872b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Create a new session token and a session state.
6882b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    IBinder sessionToken = new Binder();
68972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    SessionState sessionState = new SessionState(sessionToken, inputId, client,
69072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                            seq, callingUid, resolvedUserId);
6912b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
6922b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Add them to the global session state map of the current user.
6932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    userState.sessionStateMap.put(sessionToken, sessionState);
6942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
6952b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Also, add them to the session state map of the current service.
696d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mSessionTokens.add(sessionToken);
6973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
698d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
699d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        createSessionInternalLocked(serviceState.mService, sessionToken,
7007de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                                resolvedUserId);
7013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
702d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
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 releaseSession(IBinder sessionToken, int userId) {
7123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
7133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "releaseSession");
7153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7182b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
7193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
7273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
7283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setSurface");
7303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
7343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
7353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                surface);
7363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
7379a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setSurface", e);
7383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
741f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                if (surface != null) {
742f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    // surface is not used in TvInputManagerService.
743f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    surface.release();
744f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                }
7453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setVolume(IBinder sessionToken, float volume, int userId) {
7513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
7523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setVolume");
7543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
7583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
7593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                volume);
7603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
7619a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setVolume", e);
7623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void tune(IBinder sessionToken, final Uri channelUri, int userId) {
7713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
7723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "tune");
7743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
7753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
7763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
7773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
7783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
77931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
78031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long currentTime = System.currentTimeMillis();
78131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long channelId = ContentUris.parseId(channelUri);
78231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
78331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Close the open log entry first, if any.
78431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        UserState userState = getUserStateLocked(resolvedUserId);
78531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
786d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (sessionState.mLogUri != null) {
78731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            SomeArgs args = SomeArgs.obtain();
788d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            args.arg1 = sessionState.mLogUri;
78931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            args.arg2 = currentTime;
79031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
79131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                    .sendToTarget();
79231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        }
79331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
79431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Create a log entry and fill it later.
79531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        ContentValues values = new ContentValues();
796f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
79731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                currentTime);
798f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0);
799f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
80031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
801d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sessionState.mLogUri = mContentResolver.insert(
80231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                TvContract.WatchedPrograms.CONTENT_URI, values);
80331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SomeArgs args = SomeArgs.obtain();
804d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        args.arg1 = sessionState.mLogUri;
80531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg2 = ContentUris.parseId(channelUri);
80631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg3 = currentTime;
80731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
8083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
8099a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in tune", e);
8103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
8113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
8123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
8143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
8153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8179a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
8189a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
8199a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
8209a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                int userId) {
8219a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
8229a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
8239a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "createOverlayView");
8249a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
8259a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
8269a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
8279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
8289a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
8299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .createOverlayView(windowToken, frame);
8309a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
8319a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in createOverlayView", e);
8329a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
8339a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
8349a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
8359a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
8369a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
8379a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
8389a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
8399a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
8409a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
8419a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
8429a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
8439a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "relayoutOverlayView");
8449a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
8459a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
8469a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
8479a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
8489a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
8499a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .relayoutOverlayView(frame);
8509a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
8519a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in relayoutOverlayView", e);
8529a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
8539a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
8549a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
8559a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
8569a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
8579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
8589a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
8599a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
8609a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void removeOverlayView(IBinder sessionToken, int userId) {
8619a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
8629a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
8639a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "removeOverlayView");
8649a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
8659a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
8669a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
8679a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
8689a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
8699a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .removeOverlayView();
8709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
8719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in removeOverlayView", e);
8729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
8739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
8749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
8759a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
8769a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
8779a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
878c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
879c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
880c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
881c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
882c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
883c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
884c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
885c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
886c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
887c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
888c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
889c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.getHardwareList();
890c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
891c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
892c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
893c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
894c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
895c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
896c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware acquireTvInputHardware(int deviceId,
897c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                ITvInputHardwareCallback callback, int userId) throws RemoteException {
898c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
899c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
900c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
901c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
902c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
903c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
904c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
905c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
906c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
907c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "acquireTvInputHardware");
908c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
909c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.acquireHardware(
910c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, callback, callingUid, resolvedUserId);
911c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
912c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
913c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
914c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
915c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
916c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
917c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
918c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
919c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
920c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
921c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
922c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
923c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
924c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
925c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
926c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
927c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
928c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "releaseTvInputHardware");
929c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
930c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mTvInputHardwareManager.releaseHardware(
931c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, hardware, callingUid, resolvedUserId);
932c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
933c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
934c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
935c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
9363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
9373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class UserState {
939d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        // A mapping from the TV input id to its TvInputInfo.
940d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
9413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
94272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        // A mapping from the token of a client to its state.
94372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final Map<IBinder, ClientState> clientStateMap =
94472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                new HashMap<IBinder, ClientState>();
94572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
9463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the name of a TV input service to its state.
947d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, ServiceState> serviceStateMap =
948d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                new HashMap<String, ServiceState>();
9493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the token of a TV input session to its state.
9513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final Map<IBinder, SessionState> sessionStateMap =
9523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new HashMap<IBinder, SessionState>();
9533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
9543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
95572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    private final class ClientState implements IBinder.DeathRecipient {
95672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final List<String> mInputIds = new ArrayList<String>();
95772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
95872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
95972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private IBinder mClientToken;
96072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final int mUserId;
96172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
96272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        ClientState(IBinder clientToken, int userId) {
96372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            mClientToken = clientToken;
96472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            mUserId = userId;
96572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
96672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
96772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        public boolean isEmpty() {
96872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            return mInputIds.isEmpty() && mSessionTokens.isEmpty();
96972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
97072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
97172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        @Override
97272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        public void binderDied() {
97372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            synchronized (mLock) {
97472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                UserState userState = getUserStateLocked(mUserId);
97572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                // DO NOT remove the client state of clientStateMap in this method. It will be
97672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                // removed in releaseSessionLocked() or unregisterCallbackInternalLocked().
97772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                ClientState clientState = userState.clientStateMap.get(mClientToken);
97872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                if (clientState != null) {
97972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    while (clientState.mSessionTokens.size() > 0) {
98072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        releaseSessionLocked(
98172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                                clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
98272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    }
98372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    while (clientState.mInputIds.size() > 0) {
98472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                        unregisterCallbackInternalLocked(
98572ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                                mClientToken, clientState.mInputIds.get(0), mUserId);
98672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                    }
98772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                }
98872ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                mClientToken = null;
98972ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            }
99072ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        }
99172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim    }
99272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim
9933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceState {
99472ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final List<IBinder> mClientTokens = new ArrayList<IBinder>();
995d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
996d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ServiceConnection mConnection;
9972b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final TvInputInfo mTvInputInfo;
9983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
999d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputService mService;
1000d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceCallback mCallback;
1001d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mBound;
1002d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mAvailable;
10032b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private boolean mReconnecting;
10043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1005d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceState(TvInputInfo inputInfo, int userId) {
10062b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mTvInputInfo = inputInfo;
10072b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mConnection = new InputServiceConnection(inputInfo, userId);
10083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
10103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10112b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private final class SessionState implements IBinder.DeathRecipient {
1012d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final String mInputId;
1013d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ITvInputClient mClient;
1014d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mSeq;
1015d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mCallingUid;
10162b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final int mUserId;
101772ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private final IBinder mSessionToken;
1018d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputSession mSession;
1019d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private Uri mLogUri;
10203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
102172ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim        private SessionState(IBinder sessionToken, String inputId, ITvInputClient client, int seq,
10222b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                int callingUid, int userId) {
102372ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim            mSessionToken = sessionToken;
10242b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mInputId = inputId;
10252b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mClient = client;
10262b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mSeq = seq;
10272b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mCallingUid = callingUid;
10282b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mUserId = userId;
10292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
10302b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
10312b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        @Override
10322b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void binderDied() {
10332b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
10342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                mSession = null;
10352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (mClient != null) {
10362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    try {
10372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        mClient.onSessionReleased(mSeq);
10382b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    } catch(RemoteException e) {
10392b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        Slog.e(TAG, "error in onSessionReleased", e);
10402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
10412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
104272ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                removeSessionStateLocked(mSessionToken, mUserId);
10432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
10443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
10463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class InputServiceConnection implements ServiceConnection {
1048d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final TvInputInfo mTvInputInfo;
10493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
10503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1051d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private InputServiceConnection(TvInputInfo inputInfo, int userId) {
10523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
1053d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mTvInputInfo = inputInfo;
10543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
10573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceConnected(ComponentName name, IBinder service) {
10583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
1059d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")");
10603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
1062d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId);
1063d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                serviceState.mService = ITvInputService.Stub.asInterface(service);
10643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // Register a callback, if we need to.
106672ad7bf915ce40d8437a4ee2518ae07b73502e12Sungsoo Lim                if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) {
1067d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mCallback = new ServiceCallback(mUserId);
10683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1069d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.registerCallback(serviceState.mCallback);
10703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
10719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in registerCallback", e);
10723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
10733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
10743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // And create sessions, if any.
1076d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                for (IBinder sessionToken : serviceState.mSessionTokens) {
1077d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
10783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
10793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
10813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
10823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
10833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceDisconnected(ComponentName name) {
10843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
1085d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
10863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
10872b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!mTvInputInfo.getComponent().equals(name)) {
10882b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                throw new IllegalArgumentException("Mismatched ComponentName: "
10892b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
10902b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
10912b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
10922b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                UserState userState = getUserStateLocked(mUserId);
10932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
10942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState != null) {
10952b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mReconnecting = true;
10962b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mBound = false;
10972b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mService = null;
10982b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mCallback = null;
10992b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
11002b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send null tokens for not finishing create session events.
11012b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    for (IBinder sessionToken : serviceState.mSessionTokens) {
11022b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
11032b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        if (sessionState.mSession == null) {
11042b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            removeSessionStateLocked(sessionToken, sessionState.mUserId);
11052b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            sendSessionTokenToClientLocked(sessionState.mClient,
11062b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                    sessionState.mInputId, null, null, sessionState.mSeq,
11072b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                    sessionState.mUserId);
11082b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
11092b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
11102b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
11112b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mAvailable) {
11122b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        serviceState.mAvailable = false;
11132b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        broadcastServiceAvailabilityChangedLocked(serviceState);
11142b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
11152b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
11162b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
11172b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
11183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
11203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
11213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
11223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
11233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
11243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ServiceCallback(int userId) {
11253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
11263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
11283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
11292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void onAvailabilityChanged(String inputId, boolean isAvailable) {
11303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
1131d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
11323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + isAvailable + ")");
11333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
11343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
1135d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
11362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState.mAvailable != isAvailable) {
11372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mAvailable = isAvailable;
11382b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    broadcastServiceAvailabilityChangedLocked(serviceState);
11393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
11403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
11413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
11423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
114331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
114431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final class LogHandler extends Handler {
114531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_OPEN_ENTRY = 1;
114631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_UPDATE_ENTRY = 2;
114731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_CLOSE_ENTRY = 3;
114831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
114931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public LogHandler(Looper looper) {
115031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            super(looper);
115131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
115231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
115331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        @Override
115431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public void handleMessage(Message msg) {
115531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            switch (msg.what) {
115631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_OPEN_ENTRY: {
115731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
115831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
115931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
116031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
116131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onOpenEntry(uri, channelId, time);
116231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
116331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
116431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
116531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_UPDATE_ENTRY: {
116631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
116731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
116831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
116931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
117031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onUpdateEntry(uri, channelId, time);
117131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
117231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
117331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
117431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_CLOSE_ENTRY: {
117531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
117631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
117731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg2;
117831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onCloseEntry(uri, time);
117931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
118031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
118131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
118231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                default: {
11836a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    Slog.w(TAG, "Unhandled message code: " + msg.what);
118431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
118531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
118631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
118731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
118831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
118931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onOpenEntry(Uri uri, long channelId, long watchStarttime) {
119031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
1191f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_TITLE,
1192f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
1193f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
1194bd23fa0ba1460a8d5194fd7c700030bf9c3f6fcbJae Seo                    TvContract.Programs.COLUMN_SHORT_DESCRIPTION
119531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
1196f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND "
1197f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND "
1198f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS + ">?";
119931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] selectionArgs = {
120031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(channelId),
120131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime),
120231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime)
120331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
1204f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            String sortOrder = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC";
120531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
120631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
120731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection,
120831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        selection, selectionArgs, sortOrder);
120931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
121031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
1211f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_TITLE, cursor.getString(0));
1212f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
1213f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                            cursor.getLong(1));
121431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(2);
1215f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
1216f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, cursor.getString(3));
121731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.update(uri, values, null, null);
121831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
121931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Schedule an update when the current program ends.
122031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = SomeArgs.obtain();
122131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg1 = uri;
122231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg2 = channelId;
122331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg3 = endTime;
122431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args);
122531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    sendMessageDelayed(msg, endTime - System.currentTimeMillis());
122631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
122731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
122831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
122931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
123031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
123131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
123231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
123331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
123431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onUpdateEntry(Uri uri, long channelId, long time) {
123531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
1236f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
1237f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
1238f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_TITLE,
1239f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
1240f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS,
1241f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_DESCRIPTION
124231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
124331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
124431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
124531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(uri, projection, null, null, null);
124631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
124731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchStartTime = cursor.getLong(0);
124831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchEndTime = cursor.getLong(1);
124931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String title = cursor.getString(2);
125031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long startTime = cursor.getLong(3);
125131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(4);
125231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String description = cursor.getString(5);
125331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
125431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Do nothing if the current log entry is already closed.
125531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    if (watchEndTime > 0) {
125631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        return;
125731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    }
125831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
125931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // The current program has just ended. Create a (complete) log entry off the
126031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // current entry.
126131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
1262f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
126331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            watchStartTime);
1264f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, time);
1265f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
1266f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_TITLE, title);
1267f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime);
1268f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
1269f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, description);
127031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
127131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
127231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
127331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
127431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
127531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
127631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
127731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            // Re-open the current log entry with the next program information.
127831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            onOpenEntry(uri, channelId, time);
127931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
128031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
128131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onCloseEntry(Uri uri, long watchEndTime) {
128231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            ContentValues values = new ContentValues();
1283f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, watchEndTime);
128431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mContentResolver.update(uri, values, null, null);
128531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
128631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    }
12873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
1288