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