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