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