TvInputManagerService.java revision 6a6059a29edf31e65541b3d8927a46f5846fb0a2
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);
1373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        userState.inputList.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());
1523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.inputList.add(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()) {
1823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (state.session != null) {
1833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        state.session.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()) {
1943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (serviceState.callback != null) {
1953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
1963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        serviceState.service.unregisterCallback(serviceState.callback);
1973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
1989a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
1993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
2003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
2013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                serviceState.clients.clear();
2023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                mContext.unbindService(serviceState.connection);
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
2183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private ServiceState getServiceStateLocked(ComponentName name, int userId) {
2193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
2203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ServiceState serviceState = userState.serviceStateMap.get(name);
2213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
2227de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim            throw new IllegalStateException("Service state not found for " + name + " (userId="
2237de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    + userId + ")");
2243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return serviceState;
2263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private ITvInputSession getSessionLocked(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.
2353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
2363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new SecurityException("Illegal access to the session with token " + sessionToken
2373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + " from uid " + callingUid);
2383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ITvInputSession session = sessionState.session;
2403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (session == null) {
2413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            throw new IllegalStateException("Session not yet created for token " + sessionToken);
2423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return session;
2443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
2473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            String methodName) {
2483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
2493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                false, methodName, null);
2503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void updateServiceConnectionLocked(ComponentName name, int userId) {
2533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        UserState userState = getUserStateLocked(userId);
2543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ServiceState serviceState = userState.serviceStateMap.get(name);
2553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState == null) {
2563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return;
2573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2587de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        boolean isStateEmpty = serviceState.clients.isEmpty()
2597de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                && serviceState.sessionTokens.isEmpty();
2603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (serviceState.service == null && !isStateEmpty && userId == mCurrentUserId) {
2613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is not yet connected but its state indicates that we
2623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // have pending requests. Then, connect the service.
2633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (serviceState.bound) {
2643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // We have already bound to the service so we don't try to bind again until after we
2653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // unbind later on.
2663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                return;
2673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
2699a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.d(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
2703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + ")");
2713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(name);
2733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mContext.bindServiceAsUser(i, serviceState.connection, Context.BIND_AUTO_CREATE,
2743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    new UserHandle(userId));
2753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            serviceState.bound = true;
2763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } else if (serviceState.service != null && isStateEmpty) {
2773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the service is already connected but its state indicates that we have
2783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // nothing to do with it. Then, disconnect the service.
2793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
2809a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.d(TAG, "unbindService(name=" + name.getClassName() + ")");
2813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
2823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mContext.unbindService(serviceState.connection);
2833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            userState.serviceStateMap.remove(name);
2843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
2863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
2873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
2887de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim            final int userId) {
2897de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        final SessionState sessionState =
2907de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                getUserStateLocked(userId).sessionStateMap.get(sessionToken);
2913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (DEBUG) {
2929a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
2933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    + ")");
2943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
2956a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
2966a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
2976a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo
2983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Set up a callback to send the session token.
2993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
3003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            @Override
3013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            public void onSessionCreated(ITvInputSession session) {
3023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (DEBUG) {
3039a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    Slog.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
3043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
3063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    sessionState.session = session;
307fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    if (session == null) {
308fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                        removeSessionStateLocked(sessionToken, userId);
309fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                        sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null,
3106a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                                null, sessionState.seq, userId);
311fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    } else {
312fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                        sendSessionTokenToClientLocked(sessionState.client, sessionState.name,
3136a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                                sessionToken, channels[0], sessionState.seq, userId);
314fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    }
3156a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    channels[0].dispose();
3163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
3183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        };
3193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // Create a session. When failed, send a null token immediately.
3213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
3226a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            service.createSession(channels[1], callback);
3233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException e) {
3249a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in createSession", e);
325fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang            removeSessionStateLocked(sessionToken, userId);
3266a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null, null,
3273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    sessionState.seq, userId);
3283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3296a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo        channels[1].dispose();
3303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private void sendSessionTokenToClientLocked(ITvInputClient client, ComponentName name,
3336a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            IBinder sessionToken, InputChannel channel, int seq, int userId) {
3343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        try {
3356a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo            client.onSessionCreated(name, sessionToken, channel, seq);
3363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        } catch (RemoteException exception) {
3379a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            Slog.e(TAG, "error in onSessionCreated", exception);
3383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        if (sessionToken == null) {
3413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            // This means that the session creation failed. We might want to disconnect the service.
3423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            updateServiceConnectionLocked(name, userId);
3433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
3453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
346fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
347fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        // Remove the session state from the global session state map of the current user.
348fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        UserState userState = getUserStateLocked(userId);
349fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
350fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
35131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        // Close the open log entry, if any.
35231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        if (sessionState.logUri != null) {
35331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            SomeArgs args = SomeArgs.obtain();
35431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            args.arg1 = sessionState.logUri;
35531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            args.arg2 = System.currentTimeMillis();
35631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
35731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
35831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
3597de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        // Also remove the session token from the session token list of the current service.
360fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        ServiceState serviceState = userState.serviceStateMap.get(sessionState.name);
361fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        if (serviceState != null) {
3627de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim            serviceState.sessionTokens.remove(sessionToken);
363fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        }
364fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang        updateServiceConnectionLocked(sessionState.name, userId);
365fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang    }
366fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang
3673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class BinderService extends ITvInputManager.Stub {
3683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
3693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public List<TvInputInfo> getTvInputList(int userId) {
3703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
3713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getTvInputList");
3723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
3733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
3743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
3753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
3763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    return new ArrayList<TvInputInfo>(userState.inputList);
3773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
3793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
3803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
3813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
3823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
3833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
3843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public boolean getAvailability(final ITvInputClient client, final ComponentName name,
3853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
3863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
3873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "getAvailability");
3883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
3893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
3903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
3913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
3923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    ServiceState serviceState = userState.serviceStateMap.get(name);
3933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState != null) {
3943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We already know the status of this input service. Return the cached
3953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // status.
3963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return serviceState.available;
3973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
3983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
3993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
4003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
4013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            return false;
4033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
4063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void registerCallback(final ITvInputClient client, final ComponentName name,
4073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int userId) {
4083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "registerCallback");
4103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Create a new service callback and add it to the callback map of the current
4143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // service.
4153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
4163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    ServiceState serviceState = userState.serviceStateMap.get(name);
4173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
41831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        serviceState = new ServiceState(resolvedUserId);
4193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        userState.serviceStateMap.put(name, serviceState);
4203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    IBinder iBinder = client.asBinder();
4223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (!serviceState.clients.contains(iBinder)) {
4233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        serviceState.clients.add(iBinder);
4243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState.service != null) {
4263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        if (serviceState.callback != null) {
4273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            // We already handled.
4283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            return;
4293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
4303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        serviceState.callback = new ServiceCallback(resolvedUserId);
4313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        try {
4323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                            serviceState.service.registerCallback(serviceState.callback);
4333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        } catch (RemoteException e) {
4349a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                            Slog.e(TAG, "error in registerCallback", e);
4353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        }
4363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
4373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        updateServiceConnectionLocked(name, resolvedUserId);
4383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
4413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
4423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
4463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void unregisterCallback(ITvInputClient client, ComponentName name, int userId) {
4473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
4483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    Binder.getCallingUid(), userId, "unregisterCallback");
4493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
4533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    ServiceState serviceState = userState.serviceStateMap.get(name);
4543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
4553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
4563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Remove this client from the client list and unregister the callback.
4593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    serviceState.clients.remove(client.asBinder());
4603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (!serviceState.clients.isEmpty()) {
4613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        // We have other clients who want to keep the callback. Do this later.
4623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
4633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState.service == null || serviceState.callback == null) {
4653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
4663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
4683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        serviceState.service.unregisterCallback(serviceState.callback);
4693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
4709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in unregisterCallback", e);
4713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } finally {
4723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        serviceState.callback = null;
4733957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        updateServiceConnectionLocked(name, resolvedUserId);
4743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
4753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
4763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
4773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
4783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
4793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
4803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
4823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void createSession(final ITvInputClient client, final ComponentName name,
4833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                int seq, int userId) {
4843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
4853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
4863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "createSession");
4873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
4883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
4893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
4903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Create a new session token and a session state.
4913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    IBinder sessionToken = new Binder();
4923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    SessionState sessionState = new SessionState(name, client, seq, callingUid);
4933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    sessionState.session = null;
4943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Add them to the global session state map of the current user.
4963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    UserState userState = getUserStateLocked(resolvedUserId);
4973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userState.sessionStateMap.put(sessionToken, sessionState);
4983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
4993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Also, add them to the session state map of the current service.
5003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    ServiceState serviceState = userState.serviceStateMap.get(name);
5013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState == null) {
50231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        serviceState = new ServiceState(resolvedUserId);
5033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        userState.serviceStateMap.put(name, serviceState);
5043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5057de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    serviceState.sessionTokens.add(sessionToken);
5063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5073957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    if (serviceState.service != null) {
5083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        createSessionInternalLocked(serviceState.service, sessionToken,
5097de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                                resolvedUserId);
5103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } else {
5113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        updateServiceConnectionLocked(name, resolvedUserId);
5123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5153957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
5203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void releaseSession(IBinder sessionToken, int userId) {
5213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
5223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
5233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "releaseSession");
5243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    // Release the session.
5283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
5293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).release();
5303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
5319a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in release", e);
5323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
534fd5b72f1ed2ee74a4204eef65f560fc82f0b62feDongwon Kang                    removeSessionStateLocked(sessionToken, resolvedUserId);
5353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
5423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
5433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
5443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
5453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setSurface");
5463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5473957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
5503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
5513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                surface);
5523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
5539a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setSurface", e);
5543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5593957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
5623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void setVolume(IBinder sessionToken, float volume, int userId) {
5633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
5643957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
5653957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "setVolume");
5663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
5703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
5713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                                volume);
5723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
5739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in setVolume", e);
5743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
5753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
5763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
5773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
5783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
5793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
5803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
5813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
5823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void tune(IBinder sessionToken, final Uri channelUri, int userId) {
5833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int callingUid = Binder.getCallingUid();
5843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
5853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    userId, "tune");
5863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            final long identity = Binder.clearCallingIdentity();
5873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            try {
5883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                synchronized (mLock) {
5893957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
5903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
59131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
59231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long currentTime = System.currentTimeMillis();
59331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        long channelId = ContentUris.parseId(channelUri);
59431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
59531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Close the open log entry first, if any.
59631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        UserState userState = getUserStateLocked(resolvedUserId);
59731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
59831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        if (sessionState.logUri != null) {
59931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            SomeArgs args = SomeArgs.obtain();
60031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            args.arg1 = sessionState.logUri;
60131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            args.arg2 = currentTime;
60231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
60331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                    .sendToTarget();
60431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        }
60531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
60631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        // Create a log entry and fill it later.
60731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        ContentValues values = new ContentValues();
60831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
60931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                currentTime);
61031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, 0);
61131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
61231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
61331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        sessionState.logUri = mContentResolver.insert(
61431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                                TvContract.WatchedPrograms.CONTENT_URI, values);
61531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        SomeArgs args = SomeArgs.obtain();
61631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg1 = sessionState.logUri;
61731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg2 = ContentUris.parseId(channelUri);
61831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        args.arg3 = currentTime;
61931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
6203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
6219a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in tune", e);
6223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        return;
6233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
6243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
6253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            } finally {
6263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                Binder.restoreCallingIdentity(identity);
6273957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
6283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
6299a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
6309a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
6319a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
6329a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                int userId) {
6339a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
6349a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6359a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "createOverlayView");
6369a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
6379a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
6389a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
6399a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
6409a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
6419a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .createOverlayView(windowToken, frame);
6429a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
6439a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in createOverlayView", e);
6449a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
6459a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
6469a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
6479a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
6489a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
6499a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
6509a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
6519a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
6529a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
6539a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
6549a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6559a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "relayoutOverlayView");
6569a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
6579a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
6589a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
6599a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
6609a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
6619a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .relayoutOverlayView(frame);
6629a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
6639a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in relayoutOverlayView", e);
6649a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
6659a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
6669a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
6679a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
6689a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
6699a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
6709a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho
6719a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        @Override
6729a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        public void removeOverlayView(IBinder sessionToken, int userId) {
6739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int callingUid = Binder.getCallingUid();
6749a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
6759a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    userId, "removeOverlayView");
6769a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            final long identity = Binder.clearCallingIdentity();
6779a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            try {
6789a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                synchronized (mLock) {
6799a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    try {
6809a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
6819a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                                .removeOverlayView();
6829a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    } catch (RemoteException e) {
6839a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in removeOverlayView", e);
6849a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                    }
6859a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                }
6869a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            } finally {
6879a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Binder.restoreCallingIdentity(identity);
6889a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho            }
6899a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho        }
6903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
6913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class UserState {
6933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A list of all known TV inputs on the system.
6943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
6953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
6963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the name of a TV input service to its state.
6973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final Map<ComponentName, ServiceState> serviceStateMap =
6983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new HashMap<ComponentName, ServiceState>();
6993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        // A mapping from the token of a TV input session to its state.
7013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final Map<IBinder, SessionState> sessionStateMap =
7023957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                new HashMap<IBinder, SessionState>();
7033957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
7043957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7053957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceState {
7063957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final List<IBinder> clients = new ArrayList<IBinder>();
7077de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim        private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
7083957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final ServiceConnection connection;
7093957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7103957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private ITvInputService service;
7113957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private ServiceCallback callback;
7123957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private boolean bound;
7133957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private boolean available;
7143957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
71531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private ServiceState(int userId) {
7163957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            this.connection = new InputServiceConnection(userId);
7173957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7183957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
7193957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7203957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private static final class SessionState {
7213957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final ComponentName name;
7223957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final ITvInputClient client;
7233957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int seq;
7243957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int callingUid;
7253957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7263957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private ITvInputSession session;
72731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private Uri logUri;
7283957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7293957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private SessionState(ComponentName name, ITvInputClient client, int seq, int callingUid) {
7303957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            this.name = name;
7313957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            this.client = client;
7323957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            this.seq = seq;
7333957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            this.callingUid = callingUid;
7343957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7353957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
7363957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7373957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class InputServiceConnection implements ServiceConnection {
7383957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
7393957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7403957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private InputServiceConnection(int userId) {
7413957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
7423957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7433957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7443957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceConnected(ComponentName name, IBinder service) {
7463957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
7479a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
7483957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7493957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
7503957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                ServiceState serviceState = getServiceStateLocked(name, mUserId);
7513957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                serviceState.service = ITvInputService.Stub.asInterface(service);
7523957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7533957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // Register a callback, if we need to.
7543957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                if (!serviceState.clients.isEmpty() && serviceState.callback == null) {
7553957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    serviceState.callback = new ServiceCallback(mUserId);
7563957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    try {
7573957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        serviceState.service.registerCallback(serviceState.callback);
7583957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    } catch (RemoteException e) {
7599a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                        Slog.e(TAG, "error in registerCallback", e);
7603957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    }
7613957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7623957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7633957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                // And create sessions, if any.
7647de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                for (IBinder sessionToken : serviceState.sessionTokens) {
7657de5e234715a3baa8905afa3dd0c5009af64541fSungsoo Lim                    createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
7663957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7673957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7683957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7693957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7703957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7713957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onServiceDisconnected(ComponentName name) {
7723957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
7739a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
7743957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7753957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7763957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
7773957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7783957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
7793957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        private final int mUserId;
7803957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7813957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        ServiceCallback(int userId) {
7823957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            mUserId = userId;
7833957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
7843957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo
7853957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        @Override
7863957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        public void onAvailabilityChanged(ComponentName name, boolean isAvailable)
7873957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                throws RemoteException {
7883957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            if (DEBUG) {
7899a22f0f0a631849d9c622c642d3ab0395f77584bYoungsang Cho                Slog.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
7903957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                        + isAvailable + ")");
7913957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
7923957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            synchronized (mLock) {
7933957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                ServiceState serviceState = getServiceStateLocked(name, mUserId);
7943957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                serviceState.available = isAvailable;
7953957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                for (IBinder iBinder : serviceState.clients) {
7963957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
7973957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                    client.onAvailabilityChanged(name, isAvailable);
7983957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo                }
7993957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo            }
8003957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo        }
8013957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo    }
80231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
80331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    private final class LogHandler extends Handler {
80431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_OPEN_ENTRY = 1;
80531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_UPDATE_ENTRY = 2;
80631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private static final int MSG_CLOSE_ENTRY = 3;
80731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
80831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public LogHandler(Looper looper) {
80931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            super(looper);
81031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
81131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
81231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        @Override
81331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        public void handleMessage(Message msg) {
81431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            switch (msg.what) {
81531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_OPEN_ENTRY: {
81631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
81731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
81831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
81931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
82031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onOpenEntry(uri, channelId, time);
82131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
82231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
82331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
82431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_UPDATE_ENTRY: {
82531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
82631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
82731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long channelId = (long) args.arg2;
82831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg3;
82931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onUpdateEntry(uri, channelId, time);
83031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
83131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
83231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
83331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                case MSG_CLOSE_ENTRY: {
83431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = (SomeArgs) msg.obj;
83531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Uri uri = (Uri) args.arg1;
83631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long time = (long) args.arg2;
83731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    onCloseEntry(uri, time);
83831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.recycle();
83931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
84031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
84131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                default: {
8426a6059a29edf31e65541b3d8927a46f5846fb0a2Jae Seo                    Slog.w(TAG, "Unhandled message code: " + msg.what);
84331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    return;
84431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
84531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
84631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
84731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
84831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onOpenEntry(Uri uri, long channelId, long watchStarttime) {
84931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
85031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.Programs.TITLE,
85131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.Programs.START_TIME_UTC_MILLIS,
85231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.Programs.END_TIME_UTC_MILLIS,
85331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.Programs.DESCRIPTION
85431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
85531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String selection = TvContract.Programs.CHANNEL_ID + "=? AND "
85631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    + TvContract.Programs.START_TIME_UTC_MILLIS + "<=? AND "
85731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    + TvContract.Programs.END_TIME_UTC_MILLIS + ">?";
85831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] selectionArgs = {
85931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(channelId),
86031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime),
86131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String.valueOf(watchStarttime)
86231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
86331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String sortOrder = TvContract.Programs.START_TIME_UTC_MILLIS + " ASC";
86431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
86531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
86631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection,
86731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        selection, selectionArgs, sortOrder);
86831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
86931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
87031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.TITLE, cursor.getString(0));
87131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, cursor.getLong(1));
87231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(2);
87331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime);
87431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.DESCRIPTION, cursor.getString(3));
87531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.update(uri, values, null, null);
87631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
87731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Schedule an update when the current program ends.
87831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    SomeArgs args = SomeArgs.obtain();
87931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg1 = uri;
88031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg2 = channelId;
88131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    args.arg3 = endTime;
88231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args);
88331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    sendMessageDelayed(msg, endTime - System.currentTimeMillis());
88431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
88531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
88631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
88731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
88831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
88931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
89031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
89131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
89231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onUpdateEntry(Uri uri, long channelId, long time) {
89331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            String[] projection = {
89431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
89531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS,
89631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.TITLE,
89731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.START_TIME_UTC_MILLIS,
89831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.END_TIME_UTC_MILLIS,
89931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    TvContract.WatchedPrograms.DESCRIPTION
90031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            };
90131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            Cursor cursor = null;
90231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            try {
90331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                cursor = mContentResolver.query(uri, projection, null, null, null);
90431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null && cursor.moveToNext()) {
90531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchStartTime = cursor.getLong(0);
90631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long watchEndTime = cursor.getLong(1);
90731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String title = cursor.getString(2);
90831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long startTime = cursor.getLong(3);
90931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    long endTime = cursor.getLong(4);
91031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    String description = cursor.getString(5);
91131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
91231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // Do nothing if the current log entry is already closed.
91331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    if (watchEndTime > 0) {
91431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                        return;
91531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    }
91631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
91731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // The current program has just ended. Create a (complete) log entry off the
91831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    // current entry.
91931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    ContentValues values = new ContentValues();
92031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
92131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                            watchStartTime);
92231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, time);
92331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
92431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.TITLE, title);
92531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, startTime);
92631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime);
92731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    values.put(TvContract.WatchedPrograms.DESCRIPTION, description);
92831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
92931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
93031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            } finally {
93131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                if (cursor != null) {
93231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                    cursor.close();
93331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo                }
93431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            }
93531dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            // Re-open the current log entry with the next program information.
93631dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            onOpenEntry(uri, channelId, time);
93731dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
93831dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo
93931dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        private void onCloseEntry(Uri uri, long watchEndTime) {
94031dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            ContentValues values = new ContentValues();
94131dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, watchEndTime);
94231dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo            mContentResolver.update(uri, values, null, null);
94331dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo        }
94431dc634be3610b062fbcc4afa02607ce8f4125f5Jae Seo    }
9453957091ba8f08c02b5e781098cb955a5f697a1ffJae Seo}
946