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