TvInputManagerService.java revision d5cc4a281e7ce29d1e8687ff3394b57a3a549260
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
663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.ArrayList;
673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.HashMap;
683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.List;
693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.Map;
703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo/** This class provides a system service that manages television inputs. */
723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seopublic final class TvInputManagerService extends SystemService {
733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // STOPSHIP: Turn debugging off.
743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final boolean DEBUG = true;
753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final String TAG = "TvInputManagerService";
763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Context mContext;
78c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHardwareManager mTvInputHardwareManager;
793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final ContentResolver mContentResolver;
8131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A global lock.
833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Object mLock = new Object();
843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // ID of the current user.
863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int mCurrentUserId = UserHandle.USER_OWNER;
873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A map from user id to UserState.
893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final Handler mLogHandler;
9231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public TvInputManagerService(Context context) {
943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        super(context);
9531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext = context;
9731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        mContentResolver = context.getContentResolver();
9831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        mLogHandler = new LogHandler(IoThread.get().getLooper());
9931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
100c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mTvInputHardwareManager = new TvInputHardwareManager(context);
1013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        registerBroadcastReceivers();
10231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
1033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(mCurrentUserId, new UserState());
1053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            buildTvInputListLocked(mCurrentUserId);
1063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    @Override
1103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public void onStart() {
1113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
1123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void registerBroadcastReceivers() {
1153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageMonitor monitor = new PackageMonitor() {
1163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSomePackagesChanged() {
1183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
1193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    buildTvInputListLocked(mCurrentUserId);
1203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
1233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        monitor.register(mContext, null, UserHandle.ALL, true);
1243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        IntentFilter intentFilter = new IntentFilter();
1263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
1273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
1283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext.registerReceiverAsUser(new BroadcastReceiver() {
1293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onReceive(Context context, Intent intent) {
1313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                String action = intent.getAction();
1323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
1333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
1353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }, UserHandle.ALL, intentFilter, null, null);
1393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void buildTvInputListLocked(int userId) {
1423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
143d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        userState.inputMap.clear();
1443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1459a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        if (DEBUG) Slog.d(TAG, "buildTvInputList");
1463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageManager pm = mContext.getPackageManager();
1473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        List<ResolveInfo> services = pm.queryIntentServices(
1483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
1493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        for (ResolveInfo ri : services) {
1503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            ServiceInfo si = ri.serviceInfo;
1513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
1529a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
1533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + android.Manifest.permission.BIND_TV_INPUT);
1543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                continue;
1553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            TvInputInfo info = new TvInputInfo(ri);
1579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            if (DEBUG) Slog.d(TAG, "add " + info.getId());
158d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            userState.inputMap.put(info.getId(), info);
1593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void switchUser(int userId) {
1633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (mCurrentUserId == userId) {
1653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
1663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // final int oldUserId = mCurrentUserId;
1683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // TODO: Release services and sessions in the old user state, if needed.
1693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mCurrentUserId = userId;
1703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            UserState userState = mUserStates.get(userId);
1723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (userState == null) {
1733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                userState = new UserState();
1743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(userId, userState);
1763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            buildTvInputListLocked(userId);
1773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void removeUser(int userId) {
1813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
182b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            UserState userState = mUserStates.get(userId);
183b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            if (userState == null) {
184b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo                return;
185b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            }
1863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Release created sessions.
1873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (SessionState state : userState.sessionStateMap.values()) {
188d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (state.mSession != null) {
1893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
190d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        state.mSession.release();
1913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
1929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in release", e);
1933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
1943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.sessionStateMap.clear();
1973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Unregister all callbacks and unbind all services.
1993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (ServiceState serviceState : userState.serviceStateMap.values()) {
200d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (serviceState.mCallback != null) {
2013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
202d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.unregisterCallback(serviceState.mCallback);
2033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
2049a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
2053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
207d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                serviceState.mClients.clear();
208d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                mContext.unbindService(serviceState.mConnection);
2093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.serviceStateMap.clear();
2113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.remove(userId);
2133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private UserState getUserStateLocked(int userId) {
2173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = mUserStates.get(userId);
2183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (userState == null) {
2193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("User state not found for user ID " + userId);
2203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return userState;
2223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
224d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private ServiceState getServiceStateLocked(String inputId, int userId) {
2253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
226d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
2273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
228d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            throw new IllegalStateException("Service state not found for " + inputId + " (userId="
2297de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    + userId + ")");
2303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return serviceState;
2323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
2353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
2363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
2373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (sessionState == null) {
2383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalArgumentException("Session state not found for token " + sessionToken);
2393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Only the application that requested this session or the system can access it.
241d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
2423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new SecurityException("Illegal access to the session with token " + sessionToken
2433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + " from uid " + callingUid);
2443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        return sessionState;
2462b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
2472b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
2482b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
2492b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
250d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ITvInputSession session = sessionState.mSession;
2513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (session == null) {
2523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("Session not yet created for token " + sessionToken);
2533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return session;
2553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
2583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            String methodName) {
2593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
2603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                false, methodName, null);
2613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
263d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void updateServiceConnectionLocked(String inputId, int userId) {
2643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
265d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
2663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
2673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return;
2683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2692b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (serviceState.mReconnecting) {
2702b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!serviceState.mSessionTokens.isEmpty()) {
2712b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                // wait until all the sessions are removed.
2722b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                return;
2732b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
2742b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            serviceState.mReconnecting = false;
2752b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
276d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        boolean isStateEmpty = serviceState.mClients.isEmpty()
277d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                && serviceState.mSessionTokens.isEmpty();
278d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
2793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is not yet connected but its state indicates that we
2803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // have pending requests. Then, connect the service.
281d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            if (serviceState.mBound) {
2823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // We have already bound to the service so we don't try to bind again until after we
2833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // unbind later on.
2843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
2853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
287d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId
2883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + ")");
2893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
290d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim
291d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
292d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    userState.inputMap.get(inputId).getComponent());
293d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mContext.bindServiceAsUser(i, serviceState.mConnection, Context.BIND_AUTO_CREATE,
2943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    new UserHandle(userId));
295d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            serviceState.mBound = true;
296d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        } else if (serviceState.mService != null && isStateEmpty) {
2973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is already connected but its state indicates that we have
2983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // nothing to do with it. Then, disconnect the service.
2993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
300d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "unbindService(inputId=" + inputId + ")");
3013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
302d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mContext.unbindService(serviceState.mConnection);
303d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            userState.serviceStateMap.remove(inputId);
3043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
3087de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim            final int userId) {
3097de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        final SessionState sessionState =
3107de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                getUserStateLocked(userId).sessionStateMap.get(sessionToken);
3113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (DEBUG) {
312d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
3133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3146a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
3156a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
3166a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
3173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Set up a callback to send the session token.
3183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
3193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
3203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSessionCreated(ITvInputSession session) {
3213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (DEBUG) {
322d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")");
3233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
325d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSession = session;
326fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    if (session == null) {
327fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                        removeSessionStateLocked(sessionToken, userId);
3282b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
3292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                null, null, sessionState.mSeq, userId);
330fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    } else {
3312b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        try {
3322b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            session.asBinder().linkToDeath(sessionState, 0);
3332b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        } catch (RemoteException e) {
3342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            Slog.e(TAG, "Session is already died.");
3352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
336d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
337d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                sessionToken, channels[0], sessionState.mSeq, userId);
338fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    }
3396a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    channels[0].dispose();
3403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
342832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
343832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            @Override
344832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            public void onVideoSizeChanged(int width, int height) throws RemoteException {
345832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                synchronized (mLock) {
346832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (DEBUG) {
347832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
348832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
349832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (sessionState.mSession == null || sessionState.mClient == null) {
350832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        return;
351832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
352832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    try {
353832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        sessionState.mClient.onVideoSizeChanged(width, height, sessionState.mSeq);
354832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    } catch (RemoteException e) {
355832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.e(TAG, "error in onSessionEvent");
356832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
357832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                }
358832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            }
359832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho
360832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            @Override
361832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            public void onSessionEvent(String eventType, Bundle eventArgs) {
362832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                synchronized (mLock) {
363832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (DEBUG) {
364832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
365832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
366832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    if (sessionState.mSession == null || sessionState.mClient == null) {
367832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        return;
368832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
369832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    try {
370832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        sessionState.mClient.onSessionEvent(eventType, eventArgs,
371832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                                sessionState.mSeq);
372832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    } catch (RemoteException e) {
373832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                        Slog.e(TAG, "error in onSessionEvent");
374832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                    }
375832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho                }
376832860fb9f6b3a7188a6af2d5d67806593595800Youngsang Cho            }
3773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
3783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Create a session. When failed, send a null token immediately.
3803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
3816a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            service.createSession(channels[1], callback);
3823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException e) {
3839a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in createSession", e);
384fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang            removeSessionStateLocked(sessionToken, userId);
385d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
386d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSeq, userId);
3873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3886a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        channels[1].dispose();
3893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
391d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
3926a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            IBinder sessionToken, InputChannel channel, int seq, int userId) {
3933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
394d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            client.onSessionCreated(inputId, sessionToken, channel, seq);
3953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException exception) {
3969a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in onSessionCreated", exception);
3973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3982b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
3993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4002b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
4012b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
4022b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (sessionState.mSession != null) {
4032b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
4042b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                sessionState.mSession.release();
4052b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
4062b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.w(TAG, "session is already disapeared", e);
4072b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
4082b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            sessionState.mSession = null;
4093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4102b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        removeSessionStateLocked(sessionToken, userId);
4113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
4123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
413fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
414fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        // Remove the session state from the global session state map of the current user.
415fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        UserState userState = getUserStateLocked(userId);
416fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
417fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
41831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        // Close the open log entry, if any.
419d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (sessionState.mLogUri != null) {
42031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            SomeArgs args = SomeArgs.obtain();
421d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            args.arg1 = sessionState.mLogUri;
42231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            args.arg2 = System.currentTimeMillis();
42331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
42431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
42531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
4267de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        // Also remove the session token from the session token list of the current service.
427d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
428fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        if (serviceState != null) {
429d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            serviceState.mSessionTokens.remove(sessionToken);
430fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        }
431d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        updateServiceConnectionLocked(sessionState.mInputId, userId);
432fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    }
433fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
4342b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
4352b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        for (IBinder iBinder : serviceState.mClients) {
4362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
4372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
4382b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                client.onAvailabilityChanged(
4392b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
4402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
4412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.e(TAG, "error in onAvailabilityChanged", e);
4422b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
4432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
4442b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
4452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
4463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class BinderService extends ITvInputManager.Stub {
4473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
4483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public List<TvInputInfo> getTvInputList(int userId) {
4493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getTvInputList");
4513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
455d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    return new ArrayList<TvInputInfo>(userState.inputMap.values());
4563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
4583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
4593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
463d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public boolean getAvailability(final ITvInputClient client, final String inputId,
4643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
4653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getAvailability");
4673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
471d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
4723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState != null) {
4733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We already know the status of this input service. Return the cached
4743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // status.
475d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        return serviceState.mAvailable;
4763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
4793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
4803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return false;
4823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
485d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void registerCallback(final ITvInputClient client, final String inputId,
4863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
4873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "registerCallback");
4893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Create a new service callback and add it to the callback map of the current
4933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // service.
4943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
495d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
4963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
497d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
498d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                userState.inputMap.get(inputId), resolvedUserId);
499d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
5003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    IBinder iBinder = client.asBinder();
502d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (!serviceState.mClients.contains(iBinder)) {
503d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mClients.add(iBinder);
5043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
505d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
506d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (serviceState.mCallback != null) {
5073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            // We already handled.
5083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            return;
5093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
510d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mCallback = new ServiceCallback(resolvedUserId);
5113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        try {
512d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            serviceState.mService.registerCallback(serviceState.mCallback);
5133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        } catch (RemoteException e) {
5149a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                            Slog.e(TAG, "error in registerCallback", e);
5153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
5163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
517d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
5183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
526d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void unregisterCallback(ITvInputClient client, String inputId, int userId) {
5273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
5283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "unregisterCallback");
5293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
533d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
5343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
5353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
5363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Remove this client from the client list and unregister the callback.
539d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mClients.remove(client.asBinder());
540d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (!serviceState.mClients.isEmpty()) {
5413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We have other clients who want to keep the callback. Do this later.
5423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
5433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
544d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService == null || serviceState.mCallback == null) {
5453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
5463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
548d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.unregisterCallback(serviceState.mCallback);
5493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
5509a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
5513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } finally {
552d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mCallback = null;
553d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
5543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
562d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void createSession(final ITvInputClient client, final String inputId,
5633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int seq, int userId) {
5643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
5653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
5663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "createSession");
5673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
571d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
5723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
573d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
574d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                userState.inputMap.get(inputId), resolvedUserId);
575d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
5763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5772b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send a null token immediately while reconnecting.
5782b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mReconnecting == true) {
5792b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
5802b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        return;
5812b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
5822b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5832b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Create a new session token and a session state.
5842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    IBinder sessionToken = new Binder();
5852b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    SessionState sessionState = new SessionState(
5862b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            sessionToken, inputId, client, seq, callingUid, resolvedUserId);
5872b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5882b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Add them to the global session state map of the current user.
5892b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    userState.sessionStateMap.put(sessionToken, sessionState);
5902b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5912b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Also, add them to the session state map of the current service.
592d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mSessionTokens.add(sessionToken);
5933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
594d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
595d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        createSessionInternalLocked(serviceState.mService, sessionToken,
5967de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                                resolvedUserId);
5973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
598d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
5993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void releaseSession(IBinder sessionToken, int userId) {
6083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "releaseSession");
6113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6142b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
6153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
6233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setSurface");
6263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
6303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
6313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                surface);
6323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
6339a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setSurface", e);
6343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
637f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                if (surface != null) {
638f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    // surface is not used in TvInputManagerService.
639f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    surface.release();
640f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                }
6413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setVolume(IBinder sessionToken, float volume, int userId) {
6473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setVolume");
6503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
6543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
6553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                volume);
6563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
6579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setVolume", e);
6583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void tune(IBinder sessionToken, final Uri channelUri, int userId) {
6673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "tune");
6703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
6743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
67531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
67631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long currentTime = System.currentTimeMillis();
67731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long channelId = ContentUris.parseId(channelUri);
67831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
67931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Close the open log entry first, if any.
68031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        UserState userState = getUserStateLocked(resolvedUserId);
68131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
682d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (sessionState.mLogUri != null) {
68331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            SomeArgs args = SomeArgs.obtain();
684d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            args.arg1 = sessionState.mLogUri;
68531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            args.arg2 = currentTime;
68631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
68731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                    .sendToTarget();
68831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        }
68931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
69031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Create a log entry and fill it later.
69131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        ContentValues values = new ContentValues();
692f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
69331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                currentTime);
694f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0);
695f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                        values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
69631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
697d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sessionState.mLogUri = mContentResolver.insert(
69831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                TvContract.WatchedPrograms.CONTENT_URI, values);
69931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SomeArgs args = SomeArgs.obtain();
700d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        args.arg1 = sessionState.mLogUri;
70131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg2 = ContentUris.parseId(channelUri);
70231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg3 = currentTime;
70331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
7043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
7059a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in tune", e);
7063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
7073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
7103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
7113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7139a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
7149a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
7159a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
7169a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                int userId) {
7179a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
7189a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7199a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "createOverlayView");
7209a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
7219a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
7229a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
7239a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
7249a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
7259a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .createOverlayView(windowToken, frame);
7269a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
7279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in createOverlayView", e);
7289a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
7299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
7309a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
7319a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
7329a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
7339a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
7349a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
7359a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
7369a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
7379a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
7389a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7399a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "relayoutOverlayView");
7409a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
7419a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
7429a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
7439a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
7449a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
7459a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .relayoutOverlayView(frame);
7469a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
7479a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in relayoutOverlayView", e);
7489a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
7499a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
7509a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
7519a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
7529a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
7539a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
7549a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
7559a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
7569a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void removeOverlayView(IBinder sessionToken, int userId) {
7579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
7589a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7599a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "removeOverlayView");
7609a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
7619a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
7629a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
7639a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
7649a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
7659a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .removeOverlayView();
7669a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
7679a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in removeOverlayView", e);
7689a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
7699a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
7709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
7719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
7729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
7739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
774c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
775c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
776c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
777c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
778c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
779c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
780c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
781c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
782c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
783c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
784c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
785c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.getHardwareList();
786c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
787c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
788c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
789c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
790c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
791c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
792c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware acquireTvInputHardware(int deviceId,
793c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                ITvInputHardwareCallback callback, int userId) throws RemoteException {
794c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
795c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
796c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
797c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
798c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
799c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
800c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
801c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
802c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
803c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "acquireTvInputHardware");
804c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
805c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return mTvInputHardwareManager.acquireHardware(
806c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, callback, callingUid, resolvedUserId);
807c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
808c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
809c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
810c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
811c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
812c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
813c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
814c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
815c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mContext.checkCallingPermission(
816c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    android.Manifest.permission.TV_INPUT_HARDWARE)
817c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    != PackageManager.PERMISSION_GRANTED) {
818c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
819c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
820c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
821c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final long identity = Binder.clearCallingIdentity();
822c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int callingUid = Binder.getCallingUid();
823c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
824c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    userId, "releaseTvInputHardware");
825c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
826c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mTvInputHardwareManager.releaseHardware(
827c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        deviceId, hardware, callingUid, resolvedUserId);
828c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } finally {
829c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Binder.restoreCallingIdentity(identity);
830c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
831c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
8323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
8333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class UserState {
835d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        // A mapping from the TV input id to its TvInputInfo.
836d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
8373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the name of a TV input service to its state.
839d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, ServiceState> serviceStateMap =
840d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                new HashMap<String, ServiceState>();
8413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the token of a TV input session to its state.
8433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final Map<IBinder, SessionState> sessionStateMap =
8443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new HashMap<IBinder, SessionState>();
8453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
8463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceState {
8482b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        // TODO: need to implement DeathRecipient for clients.
849d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final List<IBinder> mClients = new ArrayList<IBinder>();
850d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
851d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ServiceConnection mConnection;
8522b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final TvInputInfo mTvInputInfo;
8533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
854d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputService mService;
855d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceCallback mCallback;
856d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mBound;
857d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mAvailable;
8582b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private boolean mReconnecting;
8593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
860d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceState(TvInputInfo inputInfo, int userId) {
8612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mTvInputInfo = inputInfo;
8622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mConnection = new InputServiceConnection(inputInfo, userId);
8633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
8653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private final class SessionState implements IBinder.DeathRecipient {
867d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final String mInputId;
868d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ITvInputClient mClient;
869d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mSeq;
870d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mCallingUid;
8712b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final int mUserId;
8722b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final IBinder mToken;
873d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputSession mSession;
874d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private Uri mLogUri;
8753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8762b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
8772b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                int callingUid, int userId) {
8782b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mToken = token;
8792b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mInputId = inputId;
8802b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mClient = client;
8812b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mSeq = seq;
8822b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mCallingUid = callingUid;
8832b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mUserId = userId;
8842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
8852b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
8862b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        @Override
8872b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void binderDied() {
8882b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
8892b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                mSession = null;
8902b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (mClient != null) {
8912b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    try {
8922b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        mClient.onSessionReleased(mSeq);
8932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    } catch(RemoteException e) {
8942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        Slog.e(TAG, "error in onSessionReleased", e);
8952b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
8962b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
8972b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                removeSessionStateLocked(mToken, mUserId);
8982b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
8993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
9013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class InputServiceConnection implements ServiceConnection {
903d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final TvInputInfo mTvInputInfo;
9043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
9053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
906d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private InputServiceConnection(TvInputInfo inputInfo, int userId) {
9073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
908d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mTvInputInfo = inputInfo;
9093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
9123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceConnected(ComponentName name, IBinder service) {
9133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
914d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")");
9153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
917d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId);
918d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                serviceState.mService = ITvInputService.Stub.asInterface(service);
9193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // Register a callback, if we need to.
921d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) {
922d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mCallback = new ServiceCallback(mUserId);
9233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
924d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.registerCallback(serviceState.mCallback);
9253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
9269a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in registerCallback", e);
9273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
9283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
9293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // And create sessions, if any.
931d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                for (IBinder sessionToken : serviceState.mSessionTokens) {
932d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
9333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
9343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
9383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceDisconnected(ComponentName name) {
9393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
940d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
9413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9422b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!mTvInputInfo.getComponent().equals(name)) {
9432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                throw new IllegalArgumentException("Mismatched ComponentName: "
9442b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
9452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
9462b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
9472b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                UserState userState = getUserStateLocked(mUserId);
9482b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
9492b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState != null) {
9502b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mReconnecting = true;
9512b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mBound = false;
9522b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mService = null;
9532b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mCallback = null;
9542b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
9552b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send null tokens for not finishing create session events.
9562b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    for (IBinder sessionToken : serviceState.mSessionTokens) {
9572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
9582b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        if (sessionState.mSession == null) {
9592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            removeSessionStateLocked(sessionToken, sessionState.mUserId);
9602b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            sendSessionTokenToClientLocked(sessionState.mClient,
9612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                    sessionState.mInputId, null, null, sessionState.mSeq,
9622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                    sessionState.mUserId);
9632b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
9642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
9652b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
9662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mAvailable) {
9672b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        serviceState.mAvailable = false;
9682b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        broadcastServiceAvailabilityChangedLocked(serviceState);
9692b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
9702b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
9712b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
9722b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
9733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
9753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
9773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
9783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ServiceCallback(int userId) {
9803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
9813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
9833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
9842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void onAvailabilityChanged(String inputId, boolean isAvailable) {
9853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
986d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
9873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + isAvailable + ")");
9883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
990d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
9912b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState.mAvailable != isAvailable) {
9922b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mAvailable = isAvailable;
9932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    broadcastServiceAvailabilityChangedLocked(serviceState);
9943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
9953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
9963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
9973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
99831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
99931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final class LogHandler extends Handler {
100031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_OPEN_ENTRY = 1;
100131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_UPDATE_ENTRY = 2;
100231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_CLOSE_ENTRY = 3;
100331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
100431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public LogHandler(Looper looper) {
100531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            super(looper);
100631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
100731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
100831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        @Override
100931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public void handleMessage(Message msg) {
101031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            switch (msg.what) {
101131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_OPEN_ENTRY: {
101231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
101331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
101431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
101531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
101631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onOpenEntry(uri, channelId, time);
101731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
101831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
101931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
102031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_UPDATE_ENTRY: {
102131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
102231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
102331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
102431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
102531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onUpdateEntry(uri, channelId, time);
102631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
102731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
102831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
102931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_CLOSE_ENTRY: {
103031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
103131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
103231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg2;
103331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onCloseEntry(uri, time);
103431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
103531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
103631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
103731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                default: {
10386a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    Slog.w(TAG, "Unhandled message code: " + msg.what);
103931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
104031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
104131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
104231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
104331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
104431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onOpenEntry(Uri uri, long channelId, long watchStarttime) {
104531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
1046f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_TITLE,
1047f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
1048f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
1049f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.Programs.COLUMN_DESCRIPTION
105031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
1051f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND "
1052f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND "
1053f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS + ">?";
105431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] selectionArgs = {
105531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(channelId),
105631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime),
105731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime)
105831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
1059f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            String sortOrder = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC";
106031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
106131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
106231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection,
106331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        selection, selectionArgs, sortOrder);
106431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
106531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
1066f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_TITLE, cursor.getString(0));
1067f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
1068f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                            cursor.getLong(1));
106931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(2);
1070f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
1071f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, cursor.getString(3));
107231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.update(uri, values, null, null);
107331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
107431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Schedule an update when the current program ends.
107531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = SomeArgs.obtain();
107631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg1 = uri;
107731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg2 = channelId;
107831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg3 = endTime;
107931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args);
108031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    sendMessageDelayed(msg, endTime - System.currentTimeMillis());
108131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
108231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
108331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
108431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
108531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
108631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
108731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
108831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
108931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onUpdateEntry(Uri uri, long channelId, long time) {
109031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
1091f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
1092f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
1093f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_TITLE,
1094f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
1095f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS,
1096f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    TvContract.WatchedPrograms.COLUMN_DESCRIPTION
109731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
109831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
109931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
110031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(uri, projection, null, null, null);
110131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
110231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchStartTime = cursor.getLong(0);
110331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchEndTime = cursor.getLong(1);
110431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String title = cursor.getString(2);
110531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long startTime = cursor.getLong(3);
110631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(4);
110731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String description = cursor.getString(5);
110831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
110931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Do nothing if the current log entry is already closed.
111031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    if (watchEndTime > 0) {
111131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        return;
111231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    }
111331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
111431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // The current program has just ended. Create a (complete) log entry off the
111531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // current entry.
111631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
1117f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
111831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            watchStartTime);
1119f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, time);
1120f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
1121f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_TITLE, title);
1122f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime);
1123f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
1124f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo                    values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, description);
112531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
112631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
112731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
112831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
112931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
113031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
113131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
113231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            // Re-open the current log entry with the next program information.
113331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            onOpenEntry(uri, channelId, time);
113431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
113531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
113631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onCloseEntry(Uri uri, long watchEndTime) {
113731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            ContentValues values = new ContentValues();
1138f5cd0b388ac31104e014f9193d06080851a09e18Jae Seo            values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, watchEndTime);
113931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mContentResolver.update(uri, values, null, null);
114031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
114131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    }
11423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
1143