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