TvInputManagerService.java revision f271eacba7997d2751c336153634fac53bc4d660
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.tv; 18 19import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; 20import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY; 21import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED; 22 23import android.app.ActivityManager; 24import android.content.BroadcastReceiver; 25import android.content.ComponentName; 26import android.content.ContentProviderOperation; 27import android.content.ContentProviderResult; 28import android.content.ContentResolver; 29import android.content.ContentUris; 30import android.content.ContentValues; 31import android.content.Context; 32import android.content.Intent; 33import android.content.IntentFilter; 34import android.content.OperationApplicationException; 35import android.content.ServiceConnection; 36import android.content.pm.ActivityInfo; 37import android.content.pm.PackageManager; 38import android.content.pm.ResolveInfo; 39import android.content.pm.ServiceInfo; 40import android.graphics.Rect; 41import android.hardware.hdmi.HdmiControlManager; 42import android.hardware.hdmi.HdmiDeviceInfo; 43import android.media.tv.ITvInputClient; 44import android.media.tv.ITvInputHardware; 45import android.media.tv.ITvInputHardwareCallback; 46import android.media.tv.ITvInputManager; 47import android.media.tv.ITvInputManagerCallback; 48import android.media.tv.ITvInputService; 49import android.media.tv.ITvInputServiceCallback; 50import android.media.tv.ITvInputSession; 51import android.media.tv.ITvInputSessionCallback; 52import android.media.tv.TvContentRating; 53import android.media.tv.TvContentRatingSystemInfo; 54import android.media.tv.TvContract; 55import android.media.tv.TvInputHardwareInfo; 56import android.media.tv.TvInputInfo; 57import android.media.tv.TvInputManager; 58import android.media.tv.TvInputService; 59import android.media.tv.TvStreamConfig; 60import android.media.tv.TvTrackInfo; 61import android.net.Uri; 62import android.os.Binder; 63import android.os.Bundle; 64import android.os.Handler; 65import android.os.IBinder; 66import android.os.Looper; 67import android.os.Message; 68import android.os.Process; 69import android.os.RemoteException; 70import android.os.UserHandle; 71import android.util.Slog; 72import android.util.SparseArray; 73import android.view.InputChannel; 74import android.view.Surface; 75 76import com.android.internal.content.PackageMonitor; 77import com.android.internal.os.SomeArgs; 78import com.android.internal.util.IndentingPrintWriter; 79import com.android.server.IoThread; 80import com.android.server.SystemService; 81 82import org.xmlpull.v1.XmlPullParserException; 83 84import java.io.FileDescriptor; 85import java.io.IOException; 86import java.io.PrintWriter; 87import java.util.ArrayList; 88import java.util.HashMap; 89import java.util.HashSet; 90import java.util.Iterator; 91import java.util.List; 92import java.util.Map; 93import java.util.Set; 94 95/** This class provides a system service that manages television inputs. */ 96public final class TvInputManagerService extends SystemService { 97 // STOPSHIP: Turn debugging off. 98 private static final boolean DEBUG = true; 99 private static final String TAG = "TvInputManagerService"; 100 101 private final Context mContext; 102 private final TvInputHardwareManager mTvInputHardwareManager; 103 104 private final ContentResolver mContentResolver; 105 106 // A global lock. 107 private final Object mLock = new Object(); 108 109 // ID of the current user. 110 private int mCurrentUserId = UserHandle.USER_OWNER; 111 112 // A map from user id to UserState. 113 private final SparseArray<UserState> mUserStates = new SparseArray<UserState>(); 114 115 private final WatchLogHandler mWatchLogHandler; 116 117 public TvInputManagerService(Context context) { 118 super(context); 119 120 mContext = context; 121 mContentResolver = context.getContentResolver(); 122 mWatchLogHandler = new WatchLogHandler(mContentResolver, IoThread.get().getLooper()); 123 124 mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener()); 125 126 synchronized (mLock) { 127 mUserStates.put(mCurrentUserId, new UserState(mContext, mCurrentUserId)); 128 } 129 } 130 131 @Override 132 public void onStart() { 133 publishBinderService(Context.TV_INPUT_SERVICE, new BinderService()); 134 } 135 136 @Override 137 public void onBootPhase(int phase) { 138 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 139 registerBroadcastReceivers(); 140 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 141 synchronized (mLock) { 142 buildTvInputListLocked(mCurrentUserId); 143 buildTvContentRatingSystemListLocked(mCurrentUserId); 144 } 145 } 146 mTvInputHardwareManager.onBootPhase(phase); 147 } 148 149 private void registerBroadcastReceivers() { 150 PackageMonitor monitor = new PackageMonitor() { 151 @Override 152 public void onSomePackagesChanged() { 153 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()"); 154 synchronized (mLock) { 155 buildTvInputListLocked(mCurrentUserId); 156 buildTvContentRatingSystemListLocked(mCurrentUserId); 157 } 158 } 159 160 @Override 161 public void onPackageRemoved(String packageName, int uid) { 162 synchronized (mLock) { 163 UserState userState = getUserStateLocked(mCurrentUserId); 164 if (!userState.packageSet.contains(packageName)) { 165 // Not a TV input package. 166 return; 167 } 168 } 169 170 ArrayList<ContentProviderOperation> operations = 171 new ArrayList<ContentProviderOperation>(); 172 173 String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?"; 174 String[] selectionArgs = { packageName }; 175 176 operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI) 177 .withSelection(selection, selectionArgs).build()); 178 operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI) 179 .withSelection(selection, selectionArgs).build()); 180 operations.add(ContentProviderOperation 181 .newDelete(TvContract.WatchedPrograms.CONTENT_URI) 182 .withSelection(selection, selectionArgs).build()); 183 184 ContentProviderResult[] results = null; 185 try { 186 results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations); 187 } catch (RemoteException | OperationApplicationException e) { 188 Slog.e(TAG, "error in applyBatch", e); 189 } 190 191 if (DEBUG) { 192 Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid 193 + ")"); 194 Slog.d(TAG, "results=" + results); 195 } 196 } 197 }; 198 monitor.register(mContext, null, UserHandle.ALL, true); 199 200 IntentFilter intentFilter = new IntentFilter(); 201 intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 202 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 203 mContext.registerReceiverAsUser(new BroadcastReceiver() { 204 @Override 205 public void onReceive(Context context, Intent intent) { 206 String action = intent.getAction(); 207 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 208 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 209 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 210 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 211 } 212 } 213 }, UserHandle.ALL, intentFilter, null, null); 214 } 215 216 private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) { 217 return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, 218 component.getPackageName()) == PackageManager.PERMISSION_GRANTED; 219 } 220 221 private void buildTvInputListLocked(int userId) { 222 UserState userState = getUserStateLocked(userId); 223 userState.packageSet.clear(); 224 225 if (DEBUG) { 226 Slog.d(TAG, "buildTvInputList"); 227 } 228 PackageManager pm = mContext.getPackageManager(); 229 List<ResolveInfo> services = pm.queryIntentServices( 230 new Intent(TvInputService.SERVICE_INTERFACE), 231 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); 232 List<TvInputInfo> inputList = new ArrayList<TvInputInfo>(); 233 for (ResolveInfo ri : services) { 234 ServiceInfo si = ri.serviceInfo; 235 if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { 236 Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission " 237 + android.Manifest.permission.BIND_TV_INPUT); 238 continue; 239 } 240 241 ComponentName component = new ComponentName(si.packageName, si.name); 242 if (hasHardwarePermission(pm, component)) { 243 ServiceState serviceState = userState.serviceStateMap.get(component); 244 if (serviceState == null) { 245 // We see this hardware TV input service for the first time; we need to 246 // prepare the ServiceState object so that we can connect to the service and 247 // let it add TvInputInfo objects to mInputList if there's any. 248 serviceState = new ServiceState(component, userId); 249 userState.serviceStateMap.put(component, serviceState); 250 updateServiceConnectionLocked(component, userId); 251 } else { 252 inputList.addAll(serviceState.inputList); 253 } 254 } else { 255 try { 256 inputList.add(TvInputInfo.createTvInputInfo(mContext, ri)); 257 } catch (XmlPullParserException | IOException e) { 258 Slog.e(TAG, "failed to load TV input " + si.name, e); 259 continue; 260 } 261 } 262 userState.packageSet.add(si.packageName); 263 } 264 265 Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>(); 266 for (TvInputInfo info : inputList) { 267 if (DEBUG) { 268 Slog.d(TAG, "add " + info.getId()); 269 } 270 TvInputState state = userState.inputMap.get(info.getId()); 271 if (state == null) { 272 state = new TvInputState(); 273 } 274 state.info = info; 275 inputMap.put(info.getId(), state); 276 } 277 278 for (String inputId : inputMap.keySet()) { 279 if (!userState.inputMap.containsKey(inputId)) { 280 notifyInputAddedLocked(userState, inputId); 281 } 282 } 283 284 for (String inputId : userState.inputMap.keySet()) { 285 if (!inputMap.containsKey(inputId)) { 286 TvInputInfo info = userState.inputMap.get(inputId).info; 287 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 288 if (serviceState != null) { 289 abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId); 290 } 291 notifyInputRemovedLocked(userState, inputId); 292 } 293 } 294 295 userState.inputMap.clear(); 296 userState.inputMap = inputMap; 297 } 298 299 private void buildTvContentRatingSystemListLocked(int userId) { 300 UserState userState = getUserStateLocked(userId); 301 userState.contentRatingSystemList.clear(); 302 303 final PackageManager pm = mContext.getPackageManager(); 304 Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS); 305 for (ResolveInfo resolveInfo : 306 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) { 307 ActivityInfo receiver = resolveInfo.activityInfo; 308 Bundle metaData = receiver.metaData; 309 if (metaData == null) { 310 continue; 311 } 312 313 int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS); 314 if (xmlResId == 0) { 315 Slog.w(TAG, "Missing meta-data '" 316 + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver " 317 + receiver.packageName + "/" + receiver.name); 318 continue; 319 } 320 userState.contentRatingSystemList.add( 321 TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId, 322 receiver.applicationInfo)); 323 } 324 } 325 326 private void switchUser(int userId) { 327 synchronized (mLock) { 328 if (mCurrentUserId == userId) { 329 return; 330 } 331 // final int oldUserId = mCurrentUserId; 332 // TODO: Release services and sessions in the old user state, if needed. 333 mCurrentUserId = userId; 334 335 UserState userState = mUserStates.get(userId); 336 if (userState == null) { 337 userState = new UserState(mContext, userId); 338 } 339 mUserStates.put(userId, userState); 340 buildTvInputListLocked(userId); 341 buildTvContentRatingSystemListLocked(userId); 342 } 343 } 344 345 private void removeUser(int userId) { 346 synchronized (mLock) { 347 UserState userState = mUserStates.get(userId); 348 if (userState == null) { 349 return; 350 } 351 // Release created sessions. 352 for (SessionState state : userState.sessionStateMap.values()) { 353 if (state.session != null) { 354 try { 355 state.session.release(); 356 } catch (RemoteException e) { 357 Slog.e(TAG, "error in release", e); 358 } 359 } 360 } 361 userState.sessionStateMap.clear(); 362 363 // Unregister all callbacks and unbind all services. 364 for (ServiceState serviceState : userState.serviceStateMap.values()) { 365 if (serviceState.callback != null) { 366 try { 367 serviceState.service.unregisterCallback(serviceState.callback); 368 } catch (RemoteException e) { 369 Slog.e(TAG, "error in unregisterCallback", e); 370 } 371 } 372 mContext.unbindService(serviceState.connection); 373 } 374 userState.serviceStateMap.clear(); 375 376 // Clear everything else. 377 userState.inputMap.clear(); 378 userState.packageSet.clear(); 379 userState.contentRatingSystemList.clear(); 380 userState.clientStateMap.clear(); 381 userState.callbackSet.clear(); 382 userState.mainSessionToken = null; 383 384 mUserStates.remove(userId); 385 } 386 } 387 388 private UserState getUserStateLocked(int userId) { 389 UserState userState = mUserStates.get(userId); 390 if (userState == null) { 391 throw new IllegalStateException("User state not found for user ID " + userId); 392 } 393 return userState; 394 } 395 396 private ServiceState getServiceStateLocked(ComponentName component, int userId) { 397 UserState userState = getUserStateLocked(userId); 398 ServiceState serviceState = userState.serviceStateMap.get(component); 399 if (serviceState == null) { 400 throw new IllegalStateException("Service state not found for " + component + " (userId=" 401 + userId + ")"); 402 } 403 return serviceState; 404 } 405 406 private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) { 407 UserState userState = getUserStateLocked(userId); 408 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 409 if (sessionState == null) { 410 throw new IllegalArgumentException("Session state not found for token " + sessionToken); 411 } 412 // Only the application that requested this session or the system can access it. 413 if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) { 414 throw new SecurityException("Illegal access to the session with token " + sessionToken 415 + " from uid " + callingUid); 416 } 417 return sessionState; 418 } 419 420 private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) { 421 return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId)); 422 } 423 424 private ITvInputSession getSessionLocked(SessionState sessionState) { 425 ITvInputSession session = sessionState.session; 426 if (session == null) { 427 throw new IllegalStateException("Session not yet created for token " 428 + sessionState.sessionToken); 429 } 430 return session; 431 } 432 433 private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId, 434 String methodName) { 435 return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false, 436 false, methodName, null); 437 } 438 439 private static boolean shouldMaintainConnection(ServiceState serviceState) { 440 return !serviceState.sessionTokens.isEmpty() || serviceState.isHardware; 441 // TODO: Find a way to maintain connection to hardware TV input service only when necessary. 442 } 443 444 private void updateServiceConnectionLocked(ComponentName component, int userId) { 445 UserState userState = getUserStateLocked(userId); 446 ServiceState serviceState = userState.serviceStateMap.get(component); 447 if (serviceState == null) { 448 return; 449 } 450 if (serviceState.reconnecting) { 451 if (!serviceState.sessionTokens.isEmpty()) { 452 // wait until all the sessions are removed. 453 return; 454 } 455 serviceState.reconnecting = false; 456 } 457 boolean maintainConnection = shouldMaintainConnection(serviceState); 458 if (serviceState.service == null && maintainConnection && userId == mCurrentUserId) { 459 // This means that the service is not yet connected but its state indicates that we 460 // have pending requests. Then, connect the service. 461 if (serviceState.bound) { 462 // We have already bound to the service so we don't try to bind again until after we 463 // unbind later on. 464 return; 465 } 466 if (DEBUG) { 467 Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")"); 468 } 469 470 Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component); 471 serviceState.bound = mContext.bindServiceAsUser( 472 i, serviceState.connection, Context.BIND_AUTO_CREATE, new UserHandle(userId)); 473 } else if (serviceState.service != null && !maintainConnection) { 474 // This means that the service is already connected but its state indicates that we have 475 // nothing to do with it. Then, disconnect the service. 476 if (DEBUG) { 477 Slog.d(TAG, "unbindService(service=" + component + ")"); 478 } 479 mContext.unbindService(serviceState.connection); 480 userState.serviceStateMap.remove(component); 481 } 482 } 483 484 private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState, 485 String inputId, int userId) { 486 // Let clients know the create session requests are failed. 487 UserState userState = getUserStateLocked(userId); 488 List<SessionState> sessionsToAbort = new ArrayList<>(); 489 for (IBinder sessionToken : serviceState.sessionTokens) { 490 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 491 if (sessionState.session == null && (inputId == null 492 || sessionState.info.getId().equals(inputId))) { 493 sessionsToAbort.add(sessionState); 494 } 495 } 496 for (SessionState sessionState : sessionsToAbort) { 497 removeSessionStateLocked(sessionState.sessionToken, sessionState.userId); 498 sendSessionTokenToClientLocked(sessionState.client, 499 sessionState.info.getId(), null, null, sessionState.seq); 500 } 501 updateServiceConnectionLocked(serviceState.component, userId); 502 } 503 504 private ClientState createClientStateLocked(IBinder clientToken, int userId) { 505 UserState userState = getUserStateLocked(userId); 506 ClientState clientState = new ClientState(clientToken, userId); 507 try { 508 clientToken.linkToDeath(clientState, 0); 509 } catch (RemoteException e) { 510 Slog.e(TAG, "client process has already died", e); 511 } 512 userState.clientStateMap.put(clientToken, clientState); 513 return clientState; 514 } 515 516 private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken, 517 int userId) { 518 UserState userState = getUserStateLocked(userId); 519 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 520 if (DEBUG) { 521 Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")"); 522 } 523 InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString()); 524 525 // Set up a callback to send the session token. 526 ITvInputSessionCallback callback = new SessionCallback(sessionState, channels); 527 528 // Create a session. When failed, send a null token immediately. 529 try { 530 service.createSession(channels[1], callback, sessionState.info.getId()); 531 } catch (RemoteException e) { 532 Slog.e(TAG, "error in createSession", e); 533 removeSessionStateLocked(sessionToken, userId); 534 sendSessionTokenToClientLocked(sessionState.client, sessionState.info.getId(), null, 535 null, sessionState.seq); 536 } 537 channels[1].dispose(); 538 } 539 540 private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId, 541 IBinder sessionToken, InputChannel channel, int seq) { 542 try { 543 client.onSessionCreated(inputId, sessionToken, channel, seq); 544 } catch (RemoteException e) { 545 Slog.e(TAG, "error in onSessionCreated", e); 546 } 547 } 548 549 private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { 550 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 551 if (sessionState.session != null) { 552 UserState userState = getUserStateLocked(userId); 553 if (sessionToken == userState.mainSessionToken) { 554 setMainLocked(sessionToken, false, callingUid, userId); 555 } 556 try { 557 sessionState.session.release(); 558 } catch (RemoteException e) { 559 Slog.e(TAG, "session process has already died", e); 560 } 561 sessionState.session = null; 562 } 563 removeSessionStateLocked(sessionToken, userId); 564 } 565 566 private void removeSessionStateLocked(IBinder sessionToken, int userId) { 567 UserState userState = getUserStateLocked(userId); 568 if (sessionToken == userState.mainSessionToken) { 569 if (DEBUG) { 570 Slog.d(TAG, "mainSessionToken=null"); 571 } 572 userState.mainSessionToken = null; 573 } 574 575 // Remove the session state from the global session state map of the current user. 576 SessionState sessionState = userState.sessionStateMap.remove(sessionToken); 577 578 if (sessionState == null) { 579 return; 580 } 581 582 // Also remove the session token from the session token list of the current client and 583 // service. 584 ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder()); 585 if (clientState != null) { 586 clientState.sessionTokens.remove(sessionToken); 587 if (clientState.isEmpty()) { 588 userState.clientStateMap.remove(sessionState.client.asBinder()); 589 } 590 } 591 592 TvInputInfo info = sessionState.info; 593 if (info != null) { 594 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 595 if (serviceState != null) { 596 serviceState.sessionTokens.remove(sessionToken); 597 } 598 } 599 updateServiceConnectionLocked(sessionState.info.getComponent(), userId); 600 601 // Log the end of watch. 602 SomeArgs args = SomeArgs.obtain(); 603 args.arg1 = sessionToken; 604 args.arg2 = System.currentTimeMillis(); 605 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget(); 606 } 607 608 private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) { 609 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 610 if (sessionState.hardwareSessionToken != null) { 611 sessionState = getSessionStateLocked(sessionState.hardwareSessionToken, 612 Process.SYSTEM_UID, userId); 613 } 614 ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId); 615 if (!serviceState.isHardware) { 616 return; 617 } 618 ITvInputSession session = getSessionLocked(sessionState); 619 try { 620 session.setMain(isMain); 621 } catch (RemoteException e) { 622 Slog.e(TAG, "error in setMain", e); 623 } 624 } 625 626 private void notifyInputAddedLocked(UserState userState, String inputId) { 627 if (DEBUG) { 628 Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")"); 629 } 630 for (ITvInputManagerCallback callback : userState.callbackSet) { 631 try { 632 callback.onInputAdded(inputId); 633 } catch (RemoteException e) { 634 Slog.e(TAG, "failed to report added input to callback", e); 635 } 636 } 637 } 638 639 private void notifyInputRemovedLocked(UserState userState, String inputId) { 640 if (DEBUG) { 641 Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")"); 642 } 643 for (ITvInputManagerCallback callback : userState.callbackSet) { 644 try { 645 callback.onInputRemoved(inputId); 646 } catch (RemoteException e) { 647 Slog.e(TAG, "failed to report removed input to callback", e); 648 } 649 } 650 } 651 652 private void notifyInputStateChangedLocked(UserState userState, String inputId, 653 int state, ITvInputManagerCallback targetCallback) { 654 if (DEBUG) { 655 Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId 656 + ", state=" + state + ")"); 657 } 658 if (targetCallback == null) { 659 for (ITvInputManagerCallback callback : userState.callbackSet) { 660 try { 661 callback.onInputStateChanged(inputId, state); 662 } catch (RemoteException e) { 663 Slog.e(TAG, "failed to report state change to callback", e); 664 } 665 } 666 } else { 667 try { 668 targetCallback.onInputStateChanged(inputId, state); 669 } catch (RemoteException e) { 670 Slog.e(TAG, "failed to report state change to callback", e); 671 } 672 } 673 } 674 675 private void setStateLocked(String inputId, int state, int userId) { 676 UserState userState = getUserStateLocked(userId); 677 TvInputState inputState = userState.inputMap.get(inputId); 678 ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent()); 679 int oldState = inputState.state; 680 inputState.state = state; 681 if (serviceState != null && serviceState.service == null 682 && shouldMaintainConnection(serviceState)) { 683 // We don't notify state change while reconnecting. It should remain disconnected. 684 return; 685 } 686 if (oldState != state) { 687 notifyInputStateChangedLocked(userState, inputId, state, null); 688 } 689 } 690 691 private final class BinderService extends ITvInputManager.Stub { 692 @Override 693 public List<TvInputInfo> getTvInputList(int userId) { 694 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 695 Binder.getCallingUid(), userId, "getTvInputList"); 696 final long identity = Binder.clearCallingIdentity(); 697 try { 698 synchronized (mLock) { 699 UserState userState = getUserStateLocked(resolvedUserId); 700 List<TvInputInfo> inputList = new ArrayList<TvInputInfo>(); 701 for (TvInputState state : userState.inputMap.values()) { 702 inputList.add(state.info); 703 } 704 return inputList; 705 } 706 } finally { 707 Binder.restoreCallingIdentity(identity); 708 } 709 } 710 711 @Override 712 public TvInputInfo getTvInputInfo(String inputId, int userId) { 713 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 714 Binder.getCallingUid(), userId, "getTvInputInfo"); 715 final long identity = Binder.clearCallingIdentity(); 716 try { 717 synchronized (mLock) { 718 UserState userState = getUserStateLocked(resolvedUserId); 719 TvInputState state = userState.inputMap.get(inputId); 720 return state == null ? null : state.info; 721 } 722 } finally { 723 Binder.restoreCallingIdentity(identity); 724 } 725 } 726 727 @Override 728 public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) { 729 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 730 Binder.getCallingUid(), userId, "getTvContentRatingSystemList"); 731 final long identity = Binder.clearCallingIdentity(); 732 try { 733 synchronized (mLock) { 734 UserState userState = getUserStateLocked(resolvedUserId); 735 return userState.contentRatingSystemList; 736 } 737 } finally { 738 Binder.restoreCallingIdentity(identity); 739 } 740 } 741 742 @Override 743 public void registerCallback(final ITvInputManagerCallback callback, int userId) { 744 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 745 Binder.getCallingUid(), userId, "registerCallback"); 746 final long identity = Binder.clearCallingIdentity(); 747 try { 748 synchronized (mLock) { 749 final UserState userState = getUserStateLocked(resolvedUserId); 750 userState.callbackSet.add(callback); 751 try { 752 callback.asBinder().linkToDeath(new IBinder.DeathRecipient() { 753 @Override 754 public void binderDied() { 755 synchronized (mLock) { 756 if (userState.callbackSet != null) { 757 userState.callbackSet.remove(callback); 758 } 759 } 760 } 761 }, 0); 762 } catch (RemoteException e) { 763 Slog.e(TAG, "client process has already died", e); 764 } 765 for (TvInputState state : userState.inputMap.values()) { 766 notifyInputStateChangedLocked(userState, state.info.getId(), state.state, 767 callback); 768 } 769 } 770 } finally { 771 Binder.restoreCallingIdentity(identity); 772 } 773 } 774 775 @Override 776 public void unregisterCallback(ITvInputManagerCallback callback, int userId) { 777 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 778 Binder.getCallingUid(), userId, "unregisterCallback"); 779 final long identity = Binder.clearCallingIdentity(); 780 try { 781 synchronized (mLock) { 782 UserState userState = getUserStateLocked(resolvedUserId); 783 userState.callbackSet.remove(callback); 784 } 785 } finally { 786 Binder.restoreCallingIdentity(identity); 787 } 788 } 789 790 @Override 791 public boolean isParentalControlsEnabled(int userId) { 792 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 793 Binder.getCallingUid(), userId, "isParentalControlsEnabled"); 794 final long identity = Binder.clearCallingIdentity(); 795 try { 796 synchronized (mLock) { 797 UserState userState = getUserStateLocked(resolvedUserId); 798 return userState.persistentDataStore.isParentalControlsEnabled(); 799 } 800 } finally { 801 Binder.restoreCallingIdentity(identity); 802 } 803 } 804 805 @Override 806 public void setParentalControlsEnabled(boolean enabled, int userId) { 807 ensureParentalControlsPermission(); 808 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 809 Binder.getCallingUid(), userId, "setParentalControlsEnabled"); 810 final long identity = Binder.clearCallingIdentity(); 811 try { 812 synchronized (mLock) { 813 UserState userState = getUserStateLocked(resolvedUserId); 814 userState.persistentDataStore.setParentalControlsEnabled(enabled); 815 } 816 } finally { 817 Binder.restoreCallingIdentity(identity); 818 } 819 } 820 821 @Override 822 public boolean isRatingBlocked(String rating, int userId) { 823 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 824 Binder.getCallingUid(), userId, "isRatingBlocked"); 825 final long identity = Binder.clearCallingIdentity(); 826 try { 827 synchronized (mLock) { 828 UserState userState = getUserStateLocked(resolvedUserId); 829 return userState.persistentDataStore.isRatingBlocked( 830 TvContentRating.unflattenFromString(rating)); 831 } 832 } finally { 833 Binder.restoreCallingIdentity(identity); 834 } 835 } 836 837 @Override 838 public List<String> getBlockedRatings(int userId) { 839 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 840 Binder.getCallingUid(), userId, "getBlockedRatings"); 841 final long identity = Binder.clearCallingIdentity(); 842 try { 843 synchronized (mLock) { 844 UserState userState = getUserStateLocked(resolvedUserId); 845 List<String> ratings = new ArrayList<String>(); 846 for (TvContentRating rating 847 : userState.persistentDataStore.getBlockedRatings()) { 848 ratings.add(rating.flattenToString()); 849 } 850 return ratings; 851 } 852 } finally { 853 Binder.restoreCallingIdentity(identity); 854 } 855 } 856 857 @Override 858 public void addBlockedRating(String rating, int userId) { 859 ensureParentalControlsPermission(); 860 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 861 Binder.getCallingUid(), userId, "addBlockedRating"); 862 final long identity = Binder.clearCallingIdentity(); 863 try { 864 synchronized (mLock) { 865 UserState userState = getUserStateLocked(resolvedUserId); 866 userState.persistentDataStore.addBlockedRating( 867 TvContentRating.unflattenFromString(rating)); 868 } 869 } finally { 870 Binder.restoreCallingIdentity(identity); 871 } 872 } 873 874 @Override 875 public void removeBlockedRating(String rating, int userId) { 876 ensureParentalControlsPermission(); 877 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 878 Binder.getCallingUid(), userId, "removeBlockedRating"); 879 final long identity = Binder.clearCallingIdentity(); 880 try { 881 synchronized (mLock) { 882 UserState userState = getUserStateLocked(resolvedUserId); 883 userState.persistentDataStore.removeBlockedRating( 884 TvContentRating.unflattenFromString(rating)); 885 } 886 } finally { 887 Binder.restoreCallingIdentity(identity); 888 } 889 } 890 891 private void ensureParentalControlsPermission() { 892 if (mContext.checkCallingPermission( 893 android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) 894 != PackageManager.PERMISSION_GRANTED) { 895 throw new SecurityException( 896 "The caller does not have parental controls permission"); 897 } 898 } 899 900 @Override 901 public void createSession(final ITvInputClient client, final String inputId, 902 int seq, int userId) { 903 final int callingUid = Binder.getCallingUid(); 904 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 905 userId, "createSession"); 906 final long identity = Binder.clearCallingIdentity(); 907 try { 908 synchronized (mLock) { 909 UserState userState = getUserStateLocked(resolvedUserId); 910 TvInputState inputState = userState.inputMap.get(inputId); 911 if (inputState == null) { 912 Slog.w(TAG, "Failed to find input state for inputId=" + inputId); 913 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 914 return; 915 } 916 TvInputInfo info = inputState.info; 917 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 918 if (serviceState == null) { 919 serviceState = new ServiceState(info.getComponent(), resolvedUserId); 920 userState.serviceStateMap.put(info.getComponent(), serviceState); 921 } 922 // Send a null token immediately while reconnecting. 923 if (serviceState.reconnecting == true) { 924 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 925 return; 926 } 927 928 // Create a new session token and a session state. 929 IBinder sessionToken = new Binder(); 930 SessionState sessionState = new SessionState(sessionToken, info, client, 931 seq, callingUid, resolvedUserId); 932 933 // Add them to the global session state map of the current user. 934 userState.sessionStateMap.put(sessionToken, sessionState); 935 936 // Also, add them to the session state map of the current service. 937 serviceState.sessionTokens.add(sessionToken); 938 939 if (serviceState.service != null) { 940 createSessionInternalLocked(serviceState.service, sessionToken, 941 resolvedUserId); 942 } else { 943 updateServiceConnectionLocked(info.getComponent(), resolvedUserId); 944 } 945 } 946 } finally { 947 Binder.restoreCallingIdentity(identity); 948 } 949 } 950 951 @Override 952 public void releaseSession(IBinder sessionToken, int userId) { 953 if (DEBUG) { 954 Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")"); 955 } 956 final int callingUid = Binder.getCallingUid(); 957 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 958 userId, "releaseSession"); 959 final long identity = Binder.clearCallingIdentity(); 960 try { 961 synchronized (mLock) { 962 releaseSessionLocked(sessionToken, callingUid, resolvedUserId); 963 } 964 } finally { 965 Binder.restoreCallingIdentity(identity); 966 } 967 } 968 969 @Override 970 public void setMainSession(IBinder sessionToken, int userId) { 971 if (DEBUG) { 972 Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")"); 973 } 974 final int callingUid = Binder.getCallingUid(); 975 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 976 userId, "setMainSession"); 977 final long identity = Binder.clearCallingIdentity(); 978 try { 979 synchronized (mLock) { 980 UserState userState = getUserStateLocked(resolvedUserId); 981 if (userState.mainSessionToken == sessionToken) { 982 return; 983 } 984 if (DEBUG) { 985 Slog.d(TAG, "mainSessionToken=" + sessionToken); 986 } 987 IBinder oldMainSessionToken = userState.mainSessionToken; 988 userState.mainSessionToken = sessionToken; 989 990 // Inform the new main session first. 991 // See {@link TvInputService.Session#onSetMain}. 992 if (sessionToken != null) { 993 setMainLocked(sessionToken, true, callingUid, userId); 994 } 995 if (oldMainSessionToken != null) { 996 setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId); 997 } 998 } 999 } finally { 1000 Binder.restoreCallingIdentity(identity); 1001 } 1002 } 1003 1004 @Override 1005 public void setSurface(IBinder sessionToken, Surface surface, int userId) { 1006 final int callingUid = Binder.getCallingUid(); 1007 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1008 userId, "setSurface"); 1009 final long identity = Binder.clearCallingIdentity(); 1010 try { 1011 synchronized (mLock) { 1012 try { 1013 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1014 resolvedUserId); 1015 if (sessionState.hardwareSessionToken == null) { 1016 getSessionLocked(sessionState).setSurface(surface); 1017 } else { 1018 getSessionLocked(sessionState.hardwareSessionToken, 1019 Process.SYSTEM_UID, resolvedUserId).setSurface(surface); 1020 } 1021 } catch (RemoteException e) { 1022 Slog.e(TAG, "error in setSurface", e); 1023 } 1024 } 1025 } finally { 1026 if (surface != null) { 1027 // surface is not used in TvInputManagerService. 1028 surface.release(); 1029 } 1030 Binder.restoreCallingIdentity(identity); 1031 } 1032 } 1033 1034 @Override 1035 public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width, 1036 int height, int userId) { 1037 final int callingUid = Binder.getCallingUid(); 1038 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1039 userId, "dispatchSurfaceChanged"); 1040 final long identity = Binder.clearCallingIdentity(); 1041 try { 1042 synchronized (mLock) { 1043 try { 1044 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1045 resolvedUserId); 1046 getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, 1047 height); 1048 if (sessionState.hardwareSessionToken != null) { 1049 getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID, 1050 resolvedUserId).dispatchSurfaceChanged(format, width, height); 1051 } 1052 } catch (RemoteException e) { 1053 Slog.e(TAG, "error in dispatchSurfaceChanged", e); 1054 } 1055 } 1056 } finally { 1057 Binder.restoreCallingIdentity(identity); 1058 } 1059 } 1060 1061 @Override 1062 public void setVolume(IBinder sessionToken, float volume, int userId) { 1063 final float REMOTE_VOLUME_ON = 1.0f; 1064 final float REMOTE_VOLUME_OFF = 0f; 1065 final int callingUid = Binder.getCallingUid(); 1066 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1067 userId, "setVolume"); 1068 final long identity = Binder.clearCallingIdentity(); 1069 try { 1070 synchronized (mLock) { 1071 try { 1072 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1073 resolvedUserId); 1074 getSessionLocked(sessionState).setVolume(volume); 1075 if (sessionState.hardwareSessionToken != null) { 1076 // Here, we let the hardware session know only whether volume is on or 1077 // off to prevent that the volume is controlled in the both side. 1078 getSessionLocked(sessionState.hardwareSessionToken, 1079 Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f) 1080 ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF); 1081 } 1082 } catch (RemoteException e) { 1083 Slog.e(TAG, "error in setVolume", e); 1084 } 1085 } 1086 } finally { 1087 Binder.restoreCallingIdentity(identity); 1088 } 1089 } 1090 1091 @Override 1092 public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) { 1093 final int callingUid = Binder.getCallingUid(); 1094 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1095 userId, "tune"); 1096 final long identity = Binder.clearCallingIdentity(); 1097 try { 1098 synchronized (mLock) { 1099 try { 1100 getSessionLocked(sessionToken, callingUid, resolvedUserId).tune( 1101 channelUri, params); 1102 if (TvContract.isChannelUriForPassthroughInput(channelUri)) { 1103 // Do not log the watch history for passthrough inputs. 1104 return; 1105 } 1106 1107 UserState userState = getUserStateLocked(resolvedUserId); 1108 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 1109 1110 // Log the start of watch. 1111 SomeArgs args = SomeArgs.obtain(); 1112 args.arg1 = sessionState.info.getComponent().getPackageName(); 1113 args.arg2 = System.currentTimeMillis(); 1114 args.arg3 = ContentUris.parseId(channelUri); 1115 args.arg4 = params; 1116 args.arg5 = sessionToken; 1117 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args) 1118 .sendToTarget(); 1119 } catch (RemoteException e) { 1120 Slog.e(TAG, "error in tune", e); 1121 return; 1122 } 1123 } 1124 } finally { 1125 Binder.restoreCallingIdentity(identity); 1126 } 1127 } 1128 1129 @Override 1130 public void requestUnblockContent( 1131 IBinder sessionToken, String unblockedRating, int userId) { 1132 final int callingUid = Binder.getCallingUid(); 1133 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1134 userId, "unblockContent"); 1135 final long identity = Binder.clearCallingIdentity(); 1136 try { 1137 synchronized (mLock) { 1138 try { 1139 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1140 .requestUnblockContent(unblockedRating); 1141 } catch (RemoteException e) { 1142 Slog.e(TAG, "error in requestUnblockContent", e); 1143 } 1144 } 1145 } finally { 1146 Binder.restoreCallingIdentity(identity); 1147 } 1148 } 1149 1150 @Override 1151 public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) { 1152 final int callingUid = Binder.getCallingUid(); 1153 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1154 userId, "setCaptionEnabled"); 1155 final long identity = Binder.clearCallingIdentity(); 1156 try { 1157 synchronized (mLock) { 1158 try { 1159 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1160 .setCaptionEnabled(enabled); 1161 } catch (RemoteException e) { 1162 Slog.e(TAG, "error in setCaptionEnabled", e); 1163 } 1164 } 1165 } finally { 1166 Binder.restoreCallingIdentity(identity); 1167 } 1168 } 1169 1170 @Override 1171 public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) { 1172 final int callingUid = Binder.getCallingUid(); 1173 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1174 userId, "selectTrack"); 1175 final long identity = Binder.clearCallingIdentity(); 1176 try { 1177 synchronized (mLock) { 1178 try { 1179 getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack( 1180 type, trackId); 1181 } catch (RemoteException e) { 1182 Slog.e(TAG, "error in selectTrack", e); 1183 } 1184 } 1185 } finally { 1186 Binder.restoreCallingIdentity(identity); 1187 } 1188 } 1189 1190 @Override 1191 public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data, 1192 int userId) { 1193 final int callingUid = Binder.getCallingUid(); 1194 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1195 userId, "sendAppPrivateCommand"); 1196 final long identity = Binder.clearCallingIdentity(); 1197 try { 1198 synchronized (mLock) { 1199 try { 1200 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1201 .appPrivateCommand(command, data); 1202 } catch (RemoteException e) { 1203 Slog.e(TAG, "error in appPrivateCommand", e); 1204 } 1205 } 1206 } finally { 1207 Binder.restoreCallingIdentity(identity); 1208 } 1209 } 1210 1211 @Override 1212 public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame, 1213 int userId) { 1214 final int callingUid = Binder.getCallingUid(); 1215 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1216 userId, "createOverlayView"); 1217 final long identity = Binder.clearCallingIdentity(); 1218 try { 1219 synchronized (mLock) { 1220 try { 1221 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1222 .createOverlayView(windowToken, frame); 1223 } catch (RemoteException e) { 1224 Slog.e(TAG, "error in createOverlayView", e); 1225 } 1226 } 1227 } finally { 1228 Binder.restoreCallingIdentity(identity); 1229 } 1230 } 1231 1232 @Override 1233 public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) { 1234 final int callingUid = Binder.getCallingUid(); 1235 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1236 userId, "relayoutOverlayView"); 1237 final long identity = Binder.clearCallingIdentity(); 1238 try { 1239 synchronized (mLock) { 1240 try { 1241 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1242 .relayoutOverlayView(frame); 1243 } catch (RemoteException e) { 1244 Slog.e(TAG, "error in relayoutOverlayView", e); 1245 } 1246 } 1247 } finally { 1248 Binder.restoreCallingIdentity(identity); 1249 } 1250 } 1251 1252 @Override 1253 public void removeOverlayView(IBinder sessionToken, int userId) { 1254 final int callingUid = Binder.getCallingUid(); 1255 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1256 userId, "removeOverlayView"); 1257 final long identity = Binder.clearCallingIdentity(); 1258 try { 1259 synchronized (mLock) { 1260 try { 1261 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1262 .removeOverlayView(); 1263 } catch (RemoteException e) { 1264 Slog.e(TAG, "error in removeOverlayView", e); 1265 } 1266 } 1267 } finally { 1268 Binder.restoreCallingIdentity(identity); 1269 } 1270 } 1271 1272 @Override 1273 public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { 1274 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1275 != PackageManager.PERMISSION_GRANTED) { 1276 return null; 1277 } 1278 1279 final long identity = Binder.clearCallingIdentity(); 1280 try { 1281 return mTvInputHardwareManager.getHardwareList(); 1282 } finally { 1283 Binder.restoreCallingIdentity(identity); 1284 } 1285 } 1286 1287 @Override 1288 public ITvInputHardware acquireTvInputHardware(int deviceId, 1289 ITvInputHardwareCallback callback, TvInputInfo info, int userId) 1290 throws RemoteException { 1291 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1292 != PackageManager.PERMISSION_GRANTED) { 1293 return null; 1294 } 1295 1296 final long identity = Binder.clearCallingIdentity(); 1297 final int callingUid = Binder.getCallingUid(); 1298 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1299 userId, "acquireTvInputHardware"); 1300 try { 1301 return mTvInputHardwareManager.acquireHardware( 1302 deviceId, callback, info, callingUid, resolvedUserId); 1303 } finally { 1304 Binder.restoreCallingIdentity(identity); 1305 } 1306 } 1307 1308 @Override 1309 public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) 1310 throws RemoteException { 1311 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1312 != PackageManager.PERMISSION_GRANTED) { 1313 return; 1314 } 1315 1316 final long identity = Binder.clearCallingIdentity(); 1317 final int callingUid = Binder.getCallingUid(); 1318 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1319 userId, "releaseTvInputHardware"); 1320 try { 1321 mTvInputHardwareManager.releaseHardware( 1322 deviceId, hardware, callingUid, resolvedUserId); 1323 } finally { 1324 Binder.restoreCallingIdentity(identity); 1325 } 1326 } 1327 1328 @Override 1329 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId) 1330 throws RemoteException { 1331 if (mContext.checkCallingPermission( 1332 android.Manifest.permission.CAPTURE_TV_INPUT) 1333 != PackageManager.PERMISSION_GRANTED) { 1334 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 1335 } 1336 1337 final long identity = Binder.clearCallingIdentity(); 1338 final int callingUid = Binder.getCallingUid(); 1339 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1340 userId, "getAvailableTvStreamConfigList"); 1341 try { 1342 return mTvInputHardwareManager.getAvailableTvStreamConfigList( 1343 inputId, callingUid, resolvedUserId); 1344 } finally { 1345 Binder.restoreCallingIdentity(identity); 1346 } 1347 } 1348 1349 @Override 1350 public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config, 1351 int userId) 1352 throws RemoteException { 1353 if (mContext.checkCallingPermission( 1354 android.Manifest.permission.CAPTURE_TV_INPUT) 1355 != PackageManager.PERMISSION_GRANTED) { 1356 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 1357 } 1358 1359 final long identity = Binder.clearCallingIdentity(); 1360 final int callingUid = Binder.getCallingUid(); 1361 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1362 userId, "captureFrame"); 1363 try { 1364 String hardwareInputId = null; 1365 synchronized (mLock) { 1366 UserState userState = getUserStateLocked(resolvedUserId); 1367 if (userState.inputMap.get(inputId) == null) { 1368 Slog.e(TAG, "input not found for " + inputId); 1369 return false; 1370 } 1371 for (SessionState sessionState : userState.sessionStateMap.values()) { 1372 if (sessionState.info.getId().equals(inputId) 1373 && sessionState.hardwareSessionToken != null) { 1374 hardwareInputId = userState.sessionStateMap.get( 1375 sessionState.hardwareSessionToken).info.getId(); 1376 break; 1377 } 1378 } 1379 } 1380 return mTvInputHardwareManager.captureFrame( 1381 (hardwareInputId != null) ? hardwareInputId : inputId, 1382 surface, config, callingUid, resolvedUserId); 1383 } finally { 1384 Binder.restoreCallingIdentity(identity); 1385 } 1386 } 1387 1388 @Override 1389 public boolean isSingleSessionActive(int userId) throws RemoteException { 1390 final long identity = Binder.clearCallingIdentity(); 1391 final int callingUid = Binder.getCallingUid(); 1392 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1393 userId, "isSingleSessionActive"); 1394 try { 1395 synchronized (mLock) { 1396 UserState userState = getUserStateLocked(resolvedUserId); 1397 if (userState.sessionStateMap.size() == 1) { 1398 return true; 1399 } 1400 else if (userState.sessionStateMap.size() == 2) { 1401 SessionState[] sessionStates = userState.sessionStateMap.values().toArray( 1402 new SessionState[0]); 1403 // Check if there is a wrapper input. 1404 if (sessionStates[0].hardwareSessionToken != null 1405 || sessionStates[1].hardwareSessionToken != null) { 1406 return true; 1407 } 1408 } 1409 return false; 1410 } 1411 } finally { 1412 Binder.restoreCallingIdentity(identity); 1413 } 1414 } 1415 1416 @Override 1417 @SuppressWarnings("resource") 1418 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 1419 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 1420 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1421 != PackageManager.PERMISSION_GRANTED) { 1422 pw.println("Permission Denial: can't dump TvInputManager from pid=" 1423 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 1424 return; 1425 } 1426 1427 synchronized (mLock) { 1428 pw.println("User Ids (Current user: " + mCurrentUserId + "):"); 1429 pw.increaseIndent(); 1430 for (int i = 0; i < mUserStates.size(); i++) { 1431 int userId = mUserStates.keyAt(i); 1432 pw.println(Integer.valueOf(userId)); 1433 } 1434 pw.decreaseIndent(); 1435 1436 for (int i = 0; i < mUserStates.size(); i++) { 1437 int userId = mUserStates.keyAt(i); 1438 UserState userState = getUserStateLocked(userId); 1439 pw.println("UserState (" + userId + "):"); 1440 pw.increaseIndent(); 1441 1442 pw.println("inputMap: inputId -> TvInputState"); 1443 pw.increaseIndent(); 1444 for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) { 1445 pw.println(entry.getKey() + ": " + entry.getValue()); 1446 } 1447 pw.decreaseIndent(); 1448 1449 pw.println("packageSet:"); 1450 pw.increaseIndent(); 1451 for (String packageName : userState.packageSet) { 1452 pw.println(packageName); 1453 } 1454 pw.decreaseIndent(); 1455 1456 pw.println("clientStateMap: ITvInputClient -> ClientState"); 1457 pw.increaseIndent(); 1458 for (Map.Entry<IBinder, ClientState> entry : 1459 userState.clientStateMap.entrySet()) { 1460 ClientState client = entry.getValue(); 1461 pw.println(entry.getKey() + ": " + client); 1462 1463 pw.increaseIndent(); 1464 1465 pw.println("sessionTokens:"); 1466 pw.increaseIndent(); 1467 for (IBinder token : client.sessionTokens) { 1468 pw.println("" + token); 1469 } 1470 pw.decreaseIndent(); 1471 1472 pw.println("clientTokens: " + client.clientToken); 1473 pw.println("userId: " + client.userId); 1474 1475 pw.decreaseIndent(); 1476 } 1477 pw.decreaseIndent(); 1478 1479 pw.println("serviceStateMap: ComponentName -> ServiceState"); 1480 pw.increaseIndent(); 1481 for (Map.Entry<ComponentName, ServiceState> entry : 1482 userState.serviceStateMap.entrySet()) { 1483 ServiceState service = entry.getValue(); 1484 pw.println(entry.getKey() + ": " + service); 1485 1486 pw.increaseIndent(); 1487 1488 pw.println("sessionTokens:"); 1489 pw.increaseIndent(); 1490 for (IBinder token : service.sessionTokens) { 1491 pw.println("" + token); 1492 } 1493 pw.decreaseIndent(); 1494 1495 pw.println("service: " + service.service); 1496 pw.println("callback: " + service.callback); 1497 pw.println("bound: " + service.bound); 1498 pw.println("reconnecting: " + service.reconnecting); 1499 1500 pw.decreaseIndent(); 1501 } 1502 pw.decreaseIndent(); 1503 1504 pw.println("sessionStateMap: ITvInputSession -> SessionState"); 1505 pw.increaseIndent(); 1506 for (Map.Entry<IBinder, SessionState> entry : 1507 userState.sessionStateMap.entrySet()) { 1508 SessionState session = entry.getValue(); 1509 pw.println(entry.getKey() + ": " + session); 1510 1511 pw.increaseIndent(); 1512 pw.println("info: " + session.info); 1513 pw.println("client: " + session.client); 1514 pw.println("seq: " + session.seq); 1515 pw.println("callingUid: " + session.callingUid); 1516 pw.println("userId: " + session.userId); 1517 pw.println("sessionToken: " + session.sessionToken); 1518 pw.println("session: " + session.session); 1519 pw.println("logUri: " + session.logUri); 1520 pw.println("hardwareSessionToken: " + session.hardwareSessionToken); 1521 pw.decreaseIndent(); 1522 } 1523 pw.decreaseIndent(); 1524 1525 pw.println("callbackSet:"); 1526 pw.increaseIndent(); 1527 for (ITvInputManagerCallback callback : userState.callbackSet) { 1528 pw.println(callback.toString()); 1529 } 1530 pw.decreaseIndent(); 1531 1532 pw.println("mainSessionToken: " + userState.mainSessionToken); 1533 pw.decreaseIndent(); 1534 } 1535 } 1536 } 1537 } 1538 1539 private static final class UserState { 1540 // A mapping from the TV input id to its TvInputState. 1541 private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>(); 1542 1543 // A set of all TV input packages. 1544 private final Set<String> packageSet = new HashSet<String>(); 1545 1546 // A list of all TV content rating systems defined. 1547 private final List<TvContentRatingSystemInfo> 1548 contentRatingSystemList = new ArrayList<TvContentRatingSystemInfo>(); 1549 1550 // A mapping from the token of a client to its state. 1551 private final Map<IBinder, ClientState> clientStateMap = 1552 new HashMap<IBinder, ClientState>(); 1553 1554 // A mapping from the name of a TV input service to its state. 1555 private final Map<ComponentName, ServiceState> serviceStateMap = 1556 new HashMap<ComponentName, ServiceState>(); 1557 1558 // A mapping from the token of a TV input session to its state. 1559 private final Map<IBinder, SessionState> sessionStateMap = 1560 new HashMap<IBinder, SessionState>(); 1561 1562 // A set of callbacks. 1563 private final Set<ITvInputManagerCallback> callbackSet = 1564 new HashSet<ITvInputManagerCallback>(); 1565 1566 // The token of a "main" TV input session. 1567 private IBinder mainSessionToken = null; 1568 1569 // Persistent data store for all internal settings maintained by the TV input manager 1570 // service. 1571 private final PersistentDataStore persistentDataStore; 1572 1573 private UserState(Context context, int userId) { 1574 persistentDataStore = new PersistentDataStore(context, userId); 1575 } 1576 } 1577 1578 private final class ClientState implements IBinder.DeathRecipient { 1579 private final List<IBinder> sessionTokens = new ArrayList<IBinder>(); 1580 1581 private IBinder clientToken; 1582 private final int userId; 1583 1584 ClientState(IBinder clientToken, int userId) { 1585 this.clientToken = clientToken; 1586 this.userId = userId; 1587 } 1588 1589 public boolean isEmpty() { 1590 return sessionTokens.isEmpty(); 1591 } 1592 1593 @Override 1594 public void binderDied() { 1595 synchronized (mLock) { 1596 UserState userState = getUserStateLocked(userId); 1597 // DO NOT remove the client state of clientStateMap in this method. It will be 1598 // removed in releaseSessionLocked(). 1599 ClientState clientState = userState.clientStateMap.get(clientToken); 1600 if (clientState != null) { 1601 while (clientState.sessionTokens.size() > 0) { 1602 releaseSessionLocked( 1603 clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId); 1604 } 1605 } 1606 clientToken = null; 1607 } 1608 } 1609 } 1610 1611 private final class ServiceState { 1612 private final List<IBinder> sessionTokens = new ArrayList<IBinder>(); 1613 private final ServiceConnection connection; 1614 private final ComponentName component; 1615 private final boolean isHardware; 1616 private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>(); 1617 1618 private ITvInputService service; 1619 private ServiceCallback callback; 1620 private boolean bound; 1621 private boolean reconnecting; 1622 1623 private ServiceState(ComponentName component, int userId) { 1624 this.component = component; 1625 this.connection = new InputServiceConnection(component, userId); 1626 this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component); 1627 } 1628 } 1629 1630 private static final class TvInputState { 1631 // A TvInputInfo object which represents the TV input. 1632 private TvInputInfo info; 1633 1634 // The state of TV input. Connected by default. 1635 private int state = INPUT_STATE_CONNECTED; 1636 1637 @Override 1638 public String toString() { 1639 return "info: " + info + "; state: " + state; 1640 } 1641 } 1642 1643 private final class SessionState implements IBinder.DeathRecipient { 1644 private final TvInputInfo info; 1645 private final ITvInputClient client; 1646 private final int seq; 1647 private final int callingUid; 1648 private final int userId; 1649 private final IBinder sessionToken; 1650 private ITvInputSession session; 1651 private Uri logUri; 1652 // Not null if this session represents an external device connected to a hardware TV input. 1653 private IBinder hardwareSessionToken; 1654 1655 private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client, 1656 int seq, int callingUid, int userId) { 1657 this.sessionToken = sessionToken; 1658 this.info = info; 1659 this.client = client; 1660 this.seq = seq; 1661 this.callingUid = callingUid; 1662 this.userId = userId; 1663 } 1664 1665 @Override 1666 public void binderDied() { 1667 synchronized (mLock) { 1668 session = null; 1669 if (client != null) { 1670 try { 1671 client.onSessionReleased(seq); 1672 } catch(RemoteException e) { 1673 Slog.e(TAG, "error in onSessionReleased", e); 1674 } 1675 } 1676 // If there are any other sessions based on this session, they should be released. 1677 UserState userState = getUserStateLocked(userId); 1678 for (SessionState sessionState : userState.sessionStateMap.values()) { 1679 if (sessionToken == sessionState.hardwareSessionToken) { 1680 releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID, 1681 userId); 1682 try { 1683 sessionState.client.onSessionReleased(sessionState.seq); 1684 } catch (RemoteException e) { 1685 Slog.e(TAG, "error in onSessionReleased", e); 1686 } 1687 } 1688 } 1689 removeSessionStateLocked(sessionToken, userId); 1690 } 1691 } 1692 } 1693 1694 private final class InputServiceConnection implements ServiceConnection { 1695 private final ComponentName mComponent; 1696 private final int mUserId; 1697 1698 private InputServiceConnection(ComponentName component, int userId) { 1699 mComponent = component; 1700 mUserId = userId; 1701 } 1702 1703 @Override 1704 public void onServiceConnected(ComponentName component, IBinder service) { 1705 if (DEBUG) { 1706 Slog.d(TAG, "onServiceConnected(component=" + component + ")"); 1707 } 1708 synchronized (mLock) { 1709 UserState userState = getUserStateLocked(mUserId); 1710 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 1711 serviceState.service = ITvInputService.Stub.asInterface(service); 1712 1713 // Register a callback, if we need to. 1714 if (serviceState.isHardware && serviceState.callback == null) { 1715 serviceState.callback = new ServiceCallback(mComponent, mUserId); 1716 try { 1717 serviceState.service.registerCallback(serviceState.callback); 1718 } catch (RemoteException e) { 1719 Slog.e(TAG, "error in registerCallback", e); 1720 } 1721 } 1722 1723 // And create sessions, if any. 1724 for (IBinder sessionToken : serviceState.sessionTokens) { 1725 createSessionInternalLocked(serviceState.service, sessionToken, mUserId); 1726 } 1727 1728 for (TvInputState inputState : userState.inputMap.values()) { 1729 if (inputState.info.getComponent().equals(component) 1730 && inputState.state != INPUT_STATE_DISCONNECTED) { 1731 notifyInputStateChangedLocked(userState, inputState.info.getId(), 1732 inputState.state, null); 1733 } 1734 } 1735 1736 if (serviceState.isHardware) { 1737 List<TvInputHardwareInfo> hardwareInfoList = 1738 mTvInputHardwareManager.getHardwareList(); 1739 for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) { 1740 try { 1741 serviceState.service.notifyHardwareAdded(hardwareInfo); 1742 } catch (RemoteException e) { 1743 Slog.e(TAG, "error in notifyHardwareAdded", e); 1744 } 1745 } 1746 1747 List<HdmiDeviceInfo> deviceInfoList = 1748 mTvInputHardwareManager.getHdmiDeviceList(); 1749 for (HdmiDeviceInfo deviceInfo : deviceInfoList) { 1750 try { 1751 serviceState.service.notifyHdmiDeviceAdded(deviceInfo); 1752 } catch (RemoteException e) { 1753 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 1754 } 1755 } 1756 } 1757 } 1758 } 1759 1760 @Override 1761 public void onServiceDisconnected(ComponentName component) { 1762 if (DEBUG) { 1763 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")"); 1764 } 1765 if (!mComponent.equals(component)) { 1766 throw new IllegalArgumentException("Mismatched ComponentName: " 1767 + mComponent + " (expected), " + component + " (actual)."); 1768 } 1769 synchronized (mLock) { 1770 UserState userState = getUserStateLocked(mUserId); 1771 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 1772 if (serviceState != null) { 1773 serviceState.reconnecting = true; 1774 serviceState.bound = false; 1775 serviceState.service = null; 1776 serviceState.callback = null; 1777 1778 abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId); 1779 1780 for (TvInputState inputState : userState.inputMap.values()) { 1781 if (inputState.info.getComponent().equals(component)) { 1782 notifyInputStateChangedLocked(userState, inputState.info.getId(), 1783 INPUT_STATE_DISCONNECTED, null); 1784 } 1785 } 1786 } 1787 } 1788 } 1789 } 1790 1791 private final class ServiceCallback extends ITvInputServiceCallback.Stub { 1792 private final ComponentName mComponent; 1793 private final int mUserId; 1794 1795 ServiceCallback(ComponentName component, int userId) { 1796 mComponent = component; 1797 mUserId = userId; 1798 } 1799 1800 private void ensureHardwarePermission() { 1801 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1802 != PackageManager.PERMISSION_GRANTED) { 1803 throw new SecurityException("The caller does not have hardware permission"); 1804 } 1805 } 1806 1807 private void ensureValidInput(TvInputInfo inputInfo) { 1808 if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) { 1809 throw new IllegalArgumentException("Invalid TvInputInfo"); 1810 } 1811 } 1812 1813 private void addTvInputLocked(TvInputInfo inputInfo) { 1814 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 1815 serviceState.inputList.add(inputInfo); 1816 buildTvInputListLocked(mUserId); 1817 } 1818 1819 @Override 1820 public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) { 1821 ensureHardwarePermission(); 1822 ensureValidInput(inputInfo); 1823 synchronized (mLock) { 1824 mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo); 1825 addTvInputLocked(inputInfo); 1826 } 1827 } 1828 1829 @Override 1830 public void addHdmiTvInput(int id, TvInputInfo inputInfo) { 1831 ensureHardwarePermission(); 1832 ensureValidInput(inputInfo); 1833 synchronized (mLock) { 1834 mTvInputHardwareManager.addHdmiTvInput(id, inputInfo); 1835 addTvInputLocked(inputInfo); 1836 } 1837 } 1838 1839 @Override 1840 public void removeTvInput(String inputId) { 1841 ensureHardwarePermission(); 1842 synchronized (mLock) { 1843 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 1844 boolean removed = false; 1845 for (Iterator<TvInputInfo> it = serviceState.inputList.iterator(); 1846 it.hasNext(); ) { 1847 if (it.next().getId().equals(inputId)) { 1848 it.remove(); 1849 removed = true; 1850 break; 1851 } 1852 } 1853 if (removed) { 1854 buildTvInputListLocked(mUserId); 1855 mTvInputHardwareManager.removeTvInput(inputId); 1856 } else { 1857 Slog.e(TAG, "failed to remove input " + inputId); 1858 } 1859 } 1860 } 1861 } 1862 1863 private final class SessionCallback extends ITvInputSessionCallback.Stub { 1864 private final SessionState sessionState; 1865 private final InputChannel[] mChannels; 1866 1867 SessionCallback(SessionState sessionState, InputChannel[] channels) { 1868 this.sessionState = sessionState; 1869 mChannels = channels; 1870 } 1871 1872 @Override 1873 public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) { 1874 if (DEBUG) { 1875 Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.info.getId() + ")"); 1876 } 1877 synchronized (mLock) { 1878 sessionState.session = session; 1879 sessionState.hardwareSessionToken = harewareSessionToken; 1880 if (session == null) { 1881 removeSessionStateLocked(sessionState.sessionToken, sessionState.userId); 1882 sendSessionTokenToClientLocked(sessionState.client, 1883 sessionState.info.getId(), null, null, sessionState.seq); 1884 } else { 1885 try { 1886 session.asBinder().linkToDeath(sessionState, 0); 1887 } catch (RemoteException e) { 1888 Slog.e(TAG, "session process has already died", e); 1889 } 1890 1891 IBinder clientToken = sessionState.client.asBinder(); 1892 UserState userState = getUserStateLocked(sessionState.userId); 1893 ClientState clientState = userState.clientStateMap.get(clientToken); 1894 if (clientState == null) { 1895 clientState = createClientStateLocked(clientToken, sessionState.userId); 1896 } 1897 clientState.sessionTokens.add(sessionState.sessionToken); 1898 1899 sendSessionTokenToClientLocked(sessionState.client, 1900 sessionState.info.getId(), sessionState.sessionToken, mChannels[0], 1901 sessionState.seq); 1902 } 1903 mChannels[0].dispose(); 1904 } 1905 } 1906 1907 @Override 1908 public void onChannelRetuned(Uri channelUri) { 1909 synchronized (mLock) { 1910 if (DEBUG) { 1911 Slog.d(TAG, "onChannelRetuned(" + channelUri + ")"); 1912 } 1913 if (sessionState.session == null || sessionState.client == null) { 1914 return; 1915 } 1916 try { 1917 // TODO: Consider adding this channel change in the watch log. When we do 1918 // that, how we can protect the watch log from malicious tv inputs should 1919 // be addressed. e.g. add a field which represents where the channel change 1920 // originated from. 1921 sessionState.client.onChannelRetuned(channelUri, sessionState.seq); 1922 } catch (RemoteException e) { 1923 Slog.e(TAG, "error in onChannelRetuned", e); 1924 } 1925 } 1926 } 1927 1928 @Override 1929 public void onTracksChanged(List<TvTrackInfo> tracks) { 1930 synchronized (mLock) { 1931 if (DEBUG) { 1932 Slog.d(TAG, "onTracksChanged(" + tracks + ")"); 1933 } 1934 if (sessionState.session == null || sessionState.client == null) { 1935 return; 1936 } 1937 try { 1938 sessionState.client.onTracksChanged(tracks, sessionState.seq); 1939 } catch (RemoteException e) { 1940 Slog.e(TAG, "error in onTracksChanged", e); 1941 } 1942 } 1943 } 1944 1945 @Override 1946 public void onTrackSelected(int type, String trackId) { 1947 synchronized (mLock) { 1948 if (DEBUG) { 1949 Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")"); 1950 } 1951 if (sessionState.session == null || sessionState.client == null) { 1952 return; 1953 } 1954 try { 1955 sessionState.client.onTrackSelected(type, trackId, sessionState.seq); 1956 } catch (RemoteException e) { 1957 Slog.e(TAG, "error in onTrackSelected", e); 1958 } 1959 } 1960 } 1961 1962 @Override 1963 public void onVideoAvailable() { 1964 synchronized (mLock) { 1965 if (DEBUG) { 1966 Slog.d(TAG, "onVideoAvailable()"); 1967 } 1968 if (sessionState.session == null || sessionState.client == null) { 1969 return; 1970 } 1971 try { 1972 sessionState.client.onVideoAvailable(sessionState.seq); 1973 } catch (RemoteException e) { 1974 Slog.e(TAG, "error in onVideoAvailable", e); 1975 } 1976 } 1977 } 1978 1979 @Override 1980 public void onVideoUnavailable(int reason) { 1981 synchronized (mLock) { 1982 if (DEBUG) { 1983 Slog.d(TAG, "onVideoUnavailable(" + reason + ")"); 1984 } 1985 if (sessionState.session == null || sessionState.client == null) { 1986 return; 1987 } 1988 try { 1989 sessionState.client.onVideoUnavailable(reason, sessionState.seq); 1990 } catch (RemoteException e) { 1991 Slog.e(TAG, "error in onVideoUnavailable", e); 1992 } 1993 } 1994 } 1995 1996 @Override 1997 public void onContentAllowed() { 1998 synchronized (mLock) { 1999 if (DEBUG) { 2000 Slog.d(TAG, "onContentAllowed()"); 2001 } 2002 if (sessionState.session == null || sessionState.client == null) { 2003 return; 2004 } 2005 try { 2006 sessionState.client.onContentAllowed(sessionState.seq); 2007 } catch (RemoteException e) { 2008 Slog.e(TAG, "error in onContentAllowed", e); 2009 } 2010 } 2011 } 2012 2013 @Override 2014 public void onContentBlocked(String rating) { 2015 synchronized (mLock) { 2016 if (DEBUG) { 2017 Slog.d(TAG, "onContentBlocked()"); 2018 } 2019 if (sessionState.session == null || sessionState.client == null) { 2020 return; 2021 } 2022 try { 2023 sessionState.client.onContentBlocked(rating, sessionState.seq); 2024 } catch (RemoteException e) { 2025 Slog.e(TAG, "error in onContentBlocked", e); 2026 } 2027 } 2028 } 2029 2030 @Override 2031 public void onLayoutSurface(int left, int top, int right, int bottom) { 2032 synchronized (mLock) { 2033 if (DEBUG) { 2034 Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top 2035 + ", right=" + right + ", bottom=" + bottom + ",)"); 2036 } 2037 if (sessionState.session == null || sessionState.client == null) { 2038 return; 2039 } 2040 try { 2041 sessionState.client.onLayoutSurface(left, top, right, bottom, sessionState.seq); 2042 } catch (RemoteException e) { 2043 Slog.e(TAG, "error in onLayoutSurface", e); 2044 } 2045 } 2046 } 2047 2048 @Override 2049 public void onSessionEvent(String eventType, Bundle eventArgs) { 2050 synchronized (mLock) { 2051 if (DEBUG) { 2052 Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")"); 2053 } 2054 if (sessionState.session == null || sessionState.client == null) { 2055 return; 2056 } 2057 try { 2058 sessionState.client.onSessionEvent(eventType, eventArgs, 2059 sessionState.seq); 2060 } catch (RemoteException e) { 2061 Slog.e(TAG, "error in onSessionEvent", e); 2062 } 2063 } 2064 } 2065 } 2066 2067 private static final class WatchLogHandler extends Handler { 2068 // There are only two kinds of watch events that can happen on the system: 2069 // 1. The current TV input session is tuned to a new channel. 2070 // 2. The session is released for some reason. 2071 // The former indicates the end of the previous log entry, if any, followed by the start of 2072 // a new entry. The latter indicates the end of the most recent entry for the given session. 2073 // Here the system supplies the database the smallest set of information only that is 2074 // sufficient to consolidate the log entries while minimizing database operations in the 2075 // system service. 2076 private static final int MSG_LOG_WATCH_START = 1; 2077 private static final int MSG_LOG_WATCH_END = 2; 2078 2079 private final ContentResolver mContentResolver; 2080 2081 public WatchLogHandler(ContentResolver contentResolver, Looper looper) { 2082 super(looper); 2083 mContentResolver = contentResolver; 2084 } 2085 2086 @Override 2087 public void handleMessage(Message msg) { 2088 switch (msg.what) { 2089 case MSG_LOG_WATCH_START: { 2090 SomeArgs args = (SomeArgs) msg.obj; 2091 String packageName = (String) args.arg1; 2092 long watchStartTime = (long) args.arg2; 2093 long channelId = (long) args.arg3; 2094 Bundle tuneParams = (Bundle) args.arg4; 2095 IBinder sessionToken = (IBinder) args.arg5; 2096 2097 ContentValues values = new ContentValues(); 2098 values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName); 2099 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 2100 watchStartTime); 2101 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 2102 if (tuneParams != null) { 2103 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS, 2104 encodeTuneParams(tuneParams)); 2105 } 2106 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN, 2107 sessionToken.toString()); 2108 2109 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 2110 args.recycle(); 2111 return; 2112 } 2113 case MSG_LOG_WATCH_END: { 2114 SomeArgs args = (SomeArgs) msg.obj; 2115 IBinder sessionToken = (IBinder) args.arg1; 2116 long watchEndTime = (long) args.arg2; 2117 2118 ContentValues values = new ContentValues(); 2119 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 2120 watchEndTime); 2121 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN, 2122 sessionToken.toString()); 2123 2124 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 2125 args.recycle(); 2126 return; 2127 } 2128 default: { 2129 Slog.w(TAG, "Unhandled message code: " + msg.what); 2130 return; 2131 } 2132 } 2133 } 2134 2135 private String encodeTuneParams(Bundle tuneParams) { 2136 StringBuilder builder = new StringBuilder(); 2137 Set<String> keySet = tuneParams.keySet(); 2138 Iterator<String> it = keySet.iterator(); 2139 while (it.hasNext()) { 2140 String key = it.next(); 2141 Object value = tuneParams.get(key); 2142 if (value == null) { 2143 continue; 2144 } 2145 builder.append(replaceEscapeCharacters(key)); 2146 builder.append("="); 2147 builder.append(replaceEscapeCharacters(value.toString())); 2148 if (it.hasNext()) { 2149 builder.append(", "); 2150 } 2151 } 2152 return builder.toString(); 2153 } 2154 2155 private String replaceEscapeCharacters(String src) { 2156 final char ESCAPE_CHARACTER = '%'; 2157 final String ENCODING_TARGET_CHARACTERS = "%=,"; 2158 StringBuilder builder = new StringBuilder(); 2159 for (char ch : src.toCharArray()) { 2160 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) { 2161 builder.append(ESCAPE_CHARACTER); 2162 } 2163 builder.append(ch); 2164 } 2165 return builder.toString(); 2166 } 2167 } 2168 2169 private final class HardwareListener implements TvInputHardwareManager.Listener { 2170 @Override 2171 public void onStateChanged(String inputId, int state) { 2172 synchronized (mLock) { 2173 setStateLocked(inputId, state, mCurrentUserId); 2174 } 2175 } 2176 2177 @Override 2178 public void onHardwareDeviceAdded(TvInputHardwareInfo info) { 2179 synchronized (mLock) { 2180 UserState userState = getUserStateLocked(mCurrentUserId); 2181 // Broadcast the event to all hardware inputs. 2182 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2183 if (!serviceState.isHardware || serviceState.service == null) continue; 2184 try { 2185 serviceState.service.notifyHardwareAdded(info); 2186 } catch (RemoteException e) { 2187 Slog.e(TAG, "error in notifyHardwareAdded", e); 2188 } 2189 } 2190 } 2191 } 2192 2193 @Override 2194 public void onHardwareDeviceRemoved(TvInputHardwareInfo info) { 2195 synchronized (mLock) { 2196 UserState userState = getUserStateLocked(mCurrentUserId); 2197 // Broadcast the event to all hardware inputs. 2198 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2199 if (!serviceState.isHardware || serviceState.service == null) continue; 2200 try { 2201 serviceState.service.notifyHardwareRemoved(info); 2202 } catch (RemoteException e) { 2203 Slog.e(TAG, "error in notifyHardwareRemoved", e); 2204 } 2205 } 2206 } 2207 } 2208 2209 @Override 2210 public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { 2211 synchronized (mLock) { 2212 UserState userState = getUserStateLocked(mCurrentUserId); 2213 // Broadcast the event to all hardware inputs. 2214 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2215 if (!serviceState.isHardware || serviceState.service == null) continue; 2216 try { 2217 serviceState.service.notifyHdmiDeviceAdded(deviceInfo); 2218 } catch (RemoteException e) { 2219 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 2220 } 2221 } 2222 } 2223 } 2224 2225 @Override 2226 public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { 2227 synchronized (mLock) { 2228 UserState userState = getUserStateLocked(mCurrentUserId); 2229 // Broadcast the event to all hardware inputs. 2230 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2231 if (!serviceState.isHardware || serviceState.service == null) continue; 2232 try { 2233 serviceState.service.notifyHdmiDeviceRemoved(deviceInfo); 2234 } catch (RemoteException e) { 2235 Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e); 2236 } 2237 } 2238 } 2239 } 2240 2241 @Override 2242 public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) { 2243 synchronized (mLock) { 2244 Integer state = null; 2245 switch (deviceInfo.getDevicePowerStatus()) { 2246 case HdmiControlManager.POWER_STATUS_ON: 2247 state = INPUT_STATE_CONNECTED; 2248 break; 2249 case HdmiControlManager.POWER_STATUS_STANDBY: 2250 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON: 2251 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY: 2252 state = INPUT_STATE_CONNECTED_STANDBY; 2253 break; 2254 case HdmiControlManager.POWER_STATUS_UNKNOWN: 2255 default: 2256 state = null; 2257 break; 2258 } 2259 if (state != null) { 2260 setStateLocked(inputId, state.intValue(), mCurrentUserId); 2261 } 2262 } 2263 } 2264 } 2265} 2266