TvInputManagerService.java revision 2b35a72a69f6fc39d21f7de9e21044d64db1380d
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;
343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.net.Uri;
353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Binder;
3631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Handler;
373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.IBinder;
3831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Looper;
3931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.os.Message;
403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.Process;
413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.RemoteException;
423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.os.UserHandle;
4331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport android.provider.TvContract;
443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.tv.ITvInputClient;
453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.tv.ITvInputManager;
463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.tv.ITvInputService;
473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.tv.ITvInputServiceCallback;
483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.tv.ITvInputSession;
493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.tv.ITvInputSessionCallback;
503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.tv.TvInputInfo;
513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.tv.TvInputService;
529a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Choimport android.util.Slog;
533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.util.SparseArray;
546a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seoimport android.view.InputChannel;
553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport android.view.Surface;
563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.internal.content.PackageMonitor;
5831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.internal.os.SomeArgs;
5931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seoimport com.android.server.IoThread;
603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport com.android.server.SystemService;
613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.ArrayList;
633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.HashMap;
643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.List;
653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seoimport java.util.Map;
663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo/** This class provides a system service that manages television inputs. */
683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seopublic final class TvInputManagerService extends SystemService {
693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // STOPSHIP: Turn debugging off.
703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final boolean DEBUG = true;
713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final String TAG = "TvInputManagerService";
723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Context mContext;
743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final ContentResolver mContentResolver;
7631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A global lock.
783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final Object mLock = new Object();
793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // ID of the current user.
813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int mCurrentUserId = UserHandle.USER_OWNER;
823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    // A map from user id to UserState.
843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final Handler mLogHandler;
8731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public TvInputManagerService(Context context) {
893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        super(context);
9031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext = context;
9231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        mContentResolver = context.getContentResolver();
9331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        mLogHandler = new LogHandler(IoThread.get().getLooper());
9431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        registerBroadcastReceivers();
9631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(mCurrentUserId, new UserState());
993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            buildTvInputListLocked(mCurrentUserId);
1003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    @Override
1043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    public void onStart() {
1053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
1063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void registerBroadcastReceivers() {
1093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageMonitor monitor = new PackageMonitor() {
1103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSomePackagesChanged() {
1123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
1133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    buildTvInputListLocked(mCurrentUserId);
1143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
1173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        monitor.register(mContext, null, UserHandle.ALL, true);
1183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        IntentFilter intentFilter = new IntentFilter();
1203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
1213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
1223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        mContext.registerReceiverAsUser(new BroadcastReceiver() {
1233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
1243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onReceive(Context context, Intent intent) {
1253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                String action = intent.getAction();
1263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
1273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
1293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
1303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }, UserHandle.ALL, intentFilter, null, null);
1333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void buildTvInputListLocked(int userId) {
1363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
137d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        userState.inputMap.clear();
1383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1399a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        if (DEBUG) Slog.d(TAG, "buildTvInputList");
1403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        PackageManager pm = mContext.getPackageManager();
1413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        List<ResolveInfo> services = pm.queryIntentServices(
1423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
1433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        for (ResolveInfo ri : services) {
1443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            ServiceInfo si = ri.serviceInfo;
1453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
1469a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
1473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + android.Manifest.permission.BIND_TV_INPUT);
1483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                continue;
1493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            TvInputInfo info = new TvInputInfo(ri);
1519a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            if (DEBUG) Slog.d(TAG, "add " + info.getId());
152d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            userState.inputMap.put(info.getId(), info);
1533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void switchUser(int userId) {
1573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
1583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (mCurrentUserId == userId) {
1593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
1603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // final int oldUserId = mCurrentUserId;
1623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // TODO: Release services and sessions in the old user state, if needed.
1633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mCurrentUserId = userId;
1643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            UserState userState = mUserStates.get(userId);
1663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (userState == null) {
1673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                userState = new UserState();
1683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.put(userId, userState);
1703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            buildTvInputListLocked(userId);
1713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
1723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
1733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void removeUser(int userId) {
1753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        synchronized (mLock) {
176b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            UserState userState = mUserStates.get(userId);
177b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            if (userState == null) {
178b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo                return;
179b06cb8870f0407f18bb1225065a93aba2a5de2bfJae Seo            }
1803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Release created sessions.
1813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (SessionState state : userState.sessionStateMap.values()) {
182d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (state.mSession != null) {
1833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
184d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        state.mSession.release();
1853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
1869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in release", e);
1873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
1883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
1893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
1903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.sessionStateMap.clear();
1913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
1923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // Unregister all callbacks and unbind all services.
1933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            for (ServiceState serviceState : userState.serviceStateMap.values()) {
194d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (serviceState.mCallback != null) {
1953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
196d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.unregisterCallback(serviceState.mCallback);
1973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
1989a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
1993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
201d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                serviceState.mClients.clear();
202d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                mContext.unbindService(serviceState.mConnection);
2033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.serviceStateMap.clear();
2053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserStates.remove(userId);
2073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private UserState getUserStateLocked(int userId) {
2113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = mUserStates.get(userId);
2123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (userState == null) {
2133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("User state not found for user ID " + userId);
2143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return userState;
2163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
218d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private ServiceState getServiceStateLocked(String inputId, int userId) {
2193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
220d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
2213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
222d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            throw new IllegalStateException("Service state not found for " + inputId + " (userId="
2237de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    + userId + ")");
2243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return serviceState;
2263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2282b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
2293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
2303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
2313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (sessionState == null) {
2323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalArgumentException("Session state not found for token " + sessionToken);
2333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Only the application that requested this session or the system can access it.
235d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
2363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new SecurityException("Illegal access to the session with token " + sessionToken
2373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + " from uid " + callingUid);
2383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2392b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        return sessionState;
2402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
2412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
2422b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
2432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
244d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ITvInputSession session = sessionState.mSession;
2453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (session == null) {
2463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("Session not yet created for token " + sessionToken);
2473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return session;
2493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
2523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            String methodName) {
2533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
2543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                false, methodName, null);
2553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
257d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void updateServiceConnectionLocked(String inputId, int userId) {
2583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
259d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(inputId);
2603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
2613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return;
2623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2632b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (serviceState.mReconnecting) {
2642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!serviceState.mSessionTokens.isEmpty()) {
2652b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                // wait until all the sessions are removed.
2662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                return;
2672b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
2682b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            serviceState.mReconnecting = false;
2692b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
270d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        boolean isStateEmpty = serviceState.mClients.isEmpty()
271d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                && serviceState.mSessionTokens.isEmpty();
272d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
2733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is not yet connected but its state indicates that we
2743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // have pending requests. Then, connect the service.
275d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            if (serviceState.mBound) {
2763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // We have already bound to the service so we don't try to bind again until after we
2773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // unbind later on.
2783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
2793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
281d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId
2823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + ")");
2833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
284d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim
285d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
286d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    userState.inputMap.get(inputId).getComponent());
287d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mContext.bindServiceAsUser(i, serviceState.mConnection, Context.BIND_AUTO_CREATE,
2883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    new UserHandle(userId));
289d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            serviceState.mBound = true;
290d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        } else if (serviceState.mService != null && isStateEmpty) {
2913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is already connected but its state indicates that we have
2923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // nothing to do with it. Then, disconnect the service.
2933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
294d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "unbindService(inputId=" + inputId + ")");
2953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
296d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mContext.unbindService(serviceState.mConnection);
297d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            userState.serviceStateMap.remove(inputId);
2983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
3027de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim            final int userId) {
3037de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        final SessionState sessionState =
3047de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                getUserStateLocked(userId).sessionStateMap.get(sessionToken);
3053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (DEBUG) {
306d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
3073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3086a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
3096a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
3106a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
3113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Set up a callback to send the session token.
3123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
3133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
3143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSessionCreated(ITvInputSession session) {
3153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (DEBUG) {
316d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")");
3173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
319d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSession = session;
320fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    if (session == null) {
321fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                        removeSessionStateLocked(sessionToken, userId);
3222b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
3232b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                null, null, sessionState.mSeq, userId);
324fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    } else {
3252b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        try {
3262b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            session.asBinder().linkToDeath(sessionState, 0);
3272b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        } catch (RemoteException e) {
3282b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            Slog.e(TAG, "Session is already died.");
3292b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
330d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
331d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                sessionToken, channels[0], sessionState.mSeq, userId);
332fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    }
3336a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    channels[0].dispose();
3343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
3363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
3373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Create a session. When failed, send a null token immediately.
3393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
3406a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            service.createSession(channels[1], callback);
3413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException e) {
3429a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in createSession", e);
343fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang            removeSessionStateLocked(sessionToken, userId);
344d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
345d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    sessionState.mSeq, userId);
3463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3476a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        channels[1].dispose();
3483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
350d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
3516a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            IBinder sessionToken, InputChannel channel, int seq, int userId) {
3523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
353d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            client.onSessionCreated(inputId, sessionToken, channel, seq);
3543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException exception) {
3559a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in onSessionCreated", exception);
3563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
3583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
3602b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
3612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        if (sessionState.mSession != null) {
3622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
3632b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                sessionState.mSession.release();
3642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
3652b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.w(TAG, "session is already disapeared", e);
3662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
3672b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            sessionState.mSession = null;
3683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3692b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        removeSessionStateLocked(sessionToken, userId);
3703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
372fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
373fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        // Remove the session state from the global session state map of the current user.
374fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        UserState userState = getUserStateLocked(userId);
375fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
376fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
37731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        // Close the open log entry, if any.
378d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        if (sessionState.mLogUri != null) {
37931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            SomeArgs args = SomeArgs.obtain();
380d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            args.arg1 = sessionState.mLogUri;
38131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            args.arg2 = System.currentTimeMillis();
38231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
38331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
38431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
3857de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        // Also remove the session token from the session token list of the current service.
386d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
387fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        if (serviceState != null) {
388d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            serviceState.mSessionTokens.remove(sessionToken);
389fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        }
390d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        updateServiceConnectionLocked(sessionState.mInputId, userId);
391fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    }
392fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
3932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
3942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        for (IBinder iBinder : serviceState.mClients) {
3952b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
3962b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            try {
3972b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                client.onAvailabilityChanged(
3982b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
3992b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            } catch (RemoteException e) {
4002b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                Slog.e(TAG, "error in onAvailabilityChanged", e);
4012b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
4022b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
4032b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    }
4042b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
4053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class BinderService extends ITvInputManager.Stub {
4063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
4073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public List<TvInputInfo> getTvInputList(int userId) {
4083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getTvInputList");
4103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
414d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    return new ArrayList<TvInputInfo>(userState.inputMap.values());
4153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
4173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
4183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
422d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public boolean getAvailability(final ITvInputClient client, final String inputId,
4233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
4243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getAvailability");
4263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
430d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
4313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState != null) {
4323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We already know the status of this input service. Return the cached
4333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // status.
434d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        return serviceState.mAvailable;
4353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
4383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
4393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return false;
4413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
444d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void registerCallback(final ITvInputClient client, final String inputId,
4453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
4463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "registerCallback");
4483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Create a new service callback and add it to the callback map of the current
4523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // service.
4533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
454d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
4553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
456d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
457d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                userState.inputMap.get(inputId), resolvedUserId);
458d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
4593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    IBinder iBinder = client.asBinder();
461d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (!serviceState.mClients.contains(iBinder)) {
462d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mClients.add(iBinder);
4633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
464d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
465d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (serviceState.mCallback != null) {
4663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            // We already handled.
4673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            return;
4683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
469d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mCallback = new ServiceCallback(resolvedUserId);
4703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        try {
471d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            serviceState.mService.registerCallback(serviceState.mCallback);
4723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        } catch (RemoteException e) {
4739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                            Slog.e(TAG, "error in registerCallback", e);
4743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
4753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
476d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
4773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
4803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
4813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
485d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void unregisterCallback(ITvInputClient client, String inputId, int userId) {
4863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "unregisterCallback");
4883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
492d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
4933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
4943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
4953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Remove this client from the client list and unregister the callback.
498d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mClients.remove(client.asBinder());
499d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (!serviceState.mClients.isEmpty()) {
5003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We have other clients who want to keep the callback. Do this later.
5013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
5023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
503d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService == null || serviceState.mCallback == null) {
5043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
5053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
507d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.unregisterCallback(serviceState.mCallback);
5083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
5099a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
5103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } finally {
511d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mCallback = null;
512d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
5133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
521d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        public void createSession(final ITvInputClient client, final String inputId,
5223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int seq, int userId) {
5233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
5243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
5253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "createSession");
5263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
530d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
5313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
532d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState = new ServiceState(
533d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                                userState.inputMap.get(inputId), resolvedUserId);
534d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        userState.serviceStateMap.put(inputId, serviceState);
5353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5362b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send a null token immediately while reconnecting.
5372b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mReconnecting == true) {
5382b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
5392b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        return;
5402b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
5412b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5422b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Create a new session token and a session state.
5432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    IBinder sessionToken = new Binder();
5442b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    SessionState sessionState = new SessionState(
5452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            sessionToken, inputId, client, seq, callingUid, resolvedUserId);
5462b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5472b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Add them to the global session state map of the current user.
5482b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    userState.sessionStateMap.put(sessionToken, sessionState);
5492b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
5502b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Also, add them to the session state map of the current service.
551d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mSessionTokens.add(sessionToken);
5523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
553d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    if (serviceState.mService != null) {
554d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        createSessionInternalLocked(serviceState.mService, sessionToken,
5557de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                                resolvedUserId);
5563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
557d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        updateServiceConnectionLocked(inputId, resolvedUserId);
5583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
5663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void releaseSession(IBinder sessionToken, int userId) {
5673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
5683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
5693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "releaseSession");
5703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5732b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
5743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
5813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
5823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
5833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
5843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setSurface");
5853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
5893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
5903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                surface);
5913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
5929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setSurface", e);
5933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
596f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                if (surface != null) {
597f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    // surface is not used in TvInputManagerService.
598f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                    surface.release();
599f836206818ce338db83a3c23c486fb8cab29cb6dYoungsang Cho                }
6003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setVolume(IBinder sessionToken, float volume, int userId) {
6063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setVolume");
6093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
6133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
6143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                volume);
6153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
6169a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setVolume", e);
6173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
6253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void tune(IBinder sessionToken, final Uri channelUri, int userId) {
6263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
6273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "tune");
6293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
6303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
6313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
6323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
6333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
63431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
63531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long currentTime = System.currentTimeMillis();
63631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long channelId = ContentUris.parseId(channelUri);
63731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
63831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Close the open log entry first, if any.
63931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        UserState userState = getUserStateLocked(resolvedUserId);
64031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
641d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        if (sessionState.mLogUri != null) {
64231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            SomeArgs args = SomeArgs.obtain();
643d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                            args.arg1 = sessionState.mLogUri;
64431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            args.arg2 = currentTime;
64531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
64631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                    .sendToTarget();
64731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        }
64831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
64931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Create a log entry and fill it later.
65031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        ContentValues values = new ContentValues();
65131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
65231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                currentTime);
65331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, 0);
65431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
65531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
656d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        sessionState.mLogUri = mContentResolver.insert(
65731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                TvContract.WatchedPrograms.CONTENT_URI, values);
65831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SomeArgs args = SomeArgs.obtain();
659d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        args.arg1 = sessionState.mLogUri;
66031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg2 = ContentUris.parseId(channelUri);
66131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg3 = currentTime;
66231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
6633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
6649a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in tune", e);
6653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
6663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
6739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
6749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
6759a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                int userId) {
6769a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
6779a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6789a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "createOverlayView");
6799a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
6809a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
6819a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
6829a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
6839a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
6849a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .createOverlayView(windowToken, frame);
6859a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
6869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in createOverlayView", e);
6879a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
6889a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
6899a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
6909a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
6919a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
6929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
6939a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
6949a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
6959a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
6969a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
6979a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6989a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "relayoutOverlayView");
6999a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
7009a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
7019a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
7029a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
7039a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
7049a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .relayoutOverlayView(frame);
7059a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
7069a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in relayoutOverlayView", e);
7079a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
7089a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
7099a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
7109a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
7119a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
7129a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
7139a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
7149a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
7159a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void removeOverlayView(IBinder sessionToken, int userId) {
7169a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
7179a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
7189a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "removeOverlayView");
7199a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
7209a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
7219a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
7229a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
7239a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
7249a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .removeOverlayView();
7259a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
7269a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in removeOverlayView", e);
7279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
7289a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
7299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
7309a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
7319a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
7329a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
7333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
7343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class UserState {
736d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        // A mapping from the TV input id to its TvInputInfo.
737d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
7383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the name of a TV input service to its state.
740d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final Map<String, ServiceState> serviceStateMap =
741d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                new HashMap<String, ServiceState>();
7423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the token of a TV input session to its state.
7443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final Map<IBinder, SessionState> sessionStateMap =
7453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new HashMap<IBinder, SessionState>();
7463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
7473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceState {
7492b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        // TODO: need to implement DeathRecipient for clients.
750d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final List<IBinder> mClients = new ArrayList<IBinder>();
751d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
752d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ServiceConnection mConnection;
7532b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final TvInputInfo mTvInputInfo;
7543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
755d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputService mService;
756d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceCallback mCallback;
757d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mBound;
758d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private boolean mAvailable;
7592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private boolean mReconnecting;
7603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
761d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ServiceState(TvInputInfo inputInfo, int userId) {
7622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mTvInputInfo = inputInfo;
7632b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mConnection = new InputServiceConnection(inputInfo, userId);
7643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
7663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7672b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim    private final class SessionState implements IBinder.DeathRecipient {
768d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final String mInputId;
769d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final ITvInputClient mClient;
770d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mSeq;
771d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final int mCallingUid;
7722b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final int mUserId;
7732b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private final IBinder mToken;
774d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private ITvInputSession mSession;
775d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private Uri mLogUri;
7763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7772b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
7782b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                int callingUid, int userId) {
7792b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mToken = token;
7802b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mInputId = inputId;
7812b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mClient = client;
7822b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mSeq = seq;
7832b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mCallingUid = callingUid;
7842b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            mUserId = userId;
7852b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        }
7862b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
7872b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        @Override
7882b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void binderDied() {
7892b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
7902b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                mSession = null;
7912b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (mClient != null) {
7922b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    try {
7932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        mClient.onSessionReleased(mSeq);
7942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    } catch(RemoteException e) {
7952b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        Slog.e(TAG, "error in onSessionReleased", e);
7962b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
7972b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
7982b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                removeSessionStateLocked(mToken, mUserId);
7992b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
8003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
8023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class InputServiceConnection implements ServiceConnection {
804d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private final TvInputInfo mTvInputInfo;
8053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
8063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
807d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim        private InputServiceConnection(TvInputInfo inputInfo, int userId) {
8083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
809d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim            mTvInputInfo = inputInfo;
8103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
8133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceConnected(ComponentName name, IBinder service) {
8143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
815d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")");
8163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
818d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId);
819d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                serviceState.mService = ITvInputService.Stub.asInterface(service);
8203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // Register a callback, if we need to.
822d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) {
823d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    serviceState.mCallback = new ServiceCallback(mUserId);
8243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
825d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                        serviceState.mService.registerCallback(serviceState.mCallback);
8263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
8279a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in registerCallback", e);
8283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
8293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // And create sessions, if any.
832d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                for (IBinder sessionToken : serviceState.mSessionTokens) {
833d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
8343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
8393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceDisconnected(ComponentName name) {
8403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
841d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
8423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8432b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            if (!mTvInputInfo.getComponent().equals(name)) {
8442b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                throw new IllegalArgumentException("Mismatched ComponentName: "
8452b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
8462b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
8472b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            synchronized (mLock) {
8482b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                UserState userState = getUserStateLocked(mUserId);
8492b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
8502b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState != null) {
8512b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mReconnecting = true;
8522b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mBound = false;
8532b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mService = null;
8542b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mCallback = null;
8552b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
8562b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    // Send null tokens for not finishing create session events.
8572b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    for (IBinder sessionToken : serviceState.mSessionTokens) {
8582b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
8592b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        if (sessionState.mSession == null) {
8602b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            removeSessionStateLocked(sessionToken, sessionState.mUserId);
8612b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                            sendSessionTokenToClientLocked(sessionState.mClient,
8622b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                    sessionState.mInputId, null, null, sessionState.mSeq,
8632b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                                    sessionState.mUserId);
8642b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        }
8652b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
8662b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim
8672b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    if (serviceState.mAvailable) {
8682b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        serviceState.mAvailable = false;
8692b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                        broadcastServiceAvailabilityChangedLocked(serviceState);
8702b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    }
8712b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
8722b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                }
8732b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim            }
8743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
8763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
8783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
8793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ServiceCallback(int userId) {
8813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
8823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
8843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
8852b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim        public void onAvailabilityChanged(String inputId, boolean isAvailable) {
8863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
887d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
8883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + isAvailable + ")");
8893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
891d6672b51c5e07ec376a61057cfbb6bb7491a76b3Sungsoo Lim                ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
8922b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                if (serviceState.mAvailable != isAvailable) {
8932b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    serviceState.mAvailable = isAvailable;
8942b35a72a69f6fc39d21f7de9e21044d64db1380dSungsoo Lim                    broadcastServiceAvailabilityChangedLocked(serviceState);
8953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
8963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
89931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
90031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final class LogHandler extends Handler {
90131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_OPEN_ENTRY = 1;
90231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_UPDATE_ENTRY = 2;
90331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_CLOSE_ENTRY = 3;
90431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
90531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public LogHandler(Looper looper) {
90631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            super(looper);
90731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
90831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
90931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        @Override
91031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public void handleMessage(Message msg) {
91131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            switch (msg.what) {
91231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_OPEN_ENTRY: {
91331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
91431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
91531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
91631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
91731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onOpenEntry(uri, channelId, time);
91831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
91931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
92031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
92131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_UPDATE_ENTRY: {
92231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
92331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
92431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
92531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
92631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onUpdateEntry(uri, channelId, time);
92731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
92831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
92931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
93031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_CLOSE_ENTRY: {
93131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
93231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
93331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg2;
93431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onCloseEntry(uri, time);
93531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
93631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
93731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
93831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                default: {
9396a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    Slog.w(TAG, "Unhandled message code: " + msg.what);
94031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
94131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
94231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
94331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
94431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
94531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onOpenEntry(Uri uri, long channelId, long watchStarttime) {
94631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
94731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.Programs.TITLE,
94831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.Programs.START_TIME_UTC_MILLIS,
94931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.Programs.END_TIME_UTC_MILLIS,
95031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.Programs.DESCRIPTION
95131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
95231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String selection = TvContract.Programs.CHANNEL_ID + "=? AND "
95331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    + TvContract.Programs.START_TIME_UTC_MILLIS + "<=? AND "
95431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    + TvContract.Programs.END_TIME_UTC_MILLIS + ">?";
95531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] selectionArgs = {
95631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(channelId),
95731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime),
95831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime)
95931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
96031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String sortOrder = TvContract.Programs.START_TIME_UTC_MILLIS + " ASC";
96131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
96231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
96331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection,
96431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        selection, selectionArgs, sortOrder);
96531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
96631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
96731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.TITLE, cursor.getString(0));
96831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, cursor.getLong(1));
96931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(2);
97031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime);
97131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.DESCRIPTION, cursor.getString(3));
97231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.update(uri, values, null, null);
97331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
97431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Schedule an update when the current program ends.
97531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = SomeArgs.obtain();
97631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg1 = uri;
97731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg2 = channelId;
97831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg3 = endTime;
97931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args);
98031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    sendMessageDelayed(msg, endTime - System.currentTimeMillis());
98131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
98231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
98331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
98431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
98531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
98631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
98731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
98831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
98931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onUpdateEntry(Uri uri, long channelId, long time) {
99031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
99131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
99231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS,
99331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.TITLE,
99431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.START_TIME_UTC_MILLIS,
99531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.END_TIME_UTC_MILLIS,
99631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.DESCRIPTION
99731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
99831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
99931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
100031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(uri, projection, null, null, null);
100131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
100231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchStartTime = cursor.getLong(0);
100331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchEndTime = cursor.getLong(1);
100431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String title = cursor.getString(2);
100531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long startTime = cursor.getLong(3);
100631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(4);
100731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String description = cursor.getString(5);
100831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
100931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Do nothing if the current log entry is already closed.
101031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    if (watchEndTime > 0) {
101131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        return;
101231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    }
101331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
101431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // The current program has just ended. Create a (complete) log entry off the
101531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // current entry.
101631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
101731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
101831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            watchStartTime);
101931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, time);
102031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
102131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.TITLE, title);
102231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, startTime);
102331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime);
102431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.DESCRIPTION, description);
102531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
102631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
102731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
102831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
102931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
103031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
103131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
103231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            // Re-open the current log entry with the next program information.
103331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            onOpenEntry(uri, channelId, time);
103431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
103531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
103631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onCloseEntry(Uri uri, long watchEndTime) {
103731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            ContentValues values = new ContentValues();
103831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, watchEndTime);
103931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mContentResolver.update(uri, values, null, null);
104031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
104131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    }
10423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
1043