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