TvInputManagerService.java revision 546c635ad9a26421fbdf54efa765b5ab0a63c191
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 // Close the open log entry, if any. 717 if (sessionState.mLogUri != null) { 718 SomeArgs args = SomeArgs.obtain(); 719 args.arg1 = sessionState.mLogUri; 720 args.arg2 = System.currentTimeMillis(); 721 mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget(); 722 } 723 724 // Also remove the session token from the session token list of the current client and 725 // service. 726 ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder()); 727 if (clientState != null) { 728 clientState.mSessionTokens.remove(sessionToken); 729 if (clientState.isEmpty()) { 730 userState.clientStateMap.remove(sessionState.mClient.asBinder()); 731 } 732 } 733 734 TvInputInfo info = sessionState.mInfo; 735 if (info != null) { 736 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 737 if (serviceState != null) { 738 serviceState.mSessionTokens.remove(sessionToken); 739 } 740 } 741 updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId); 742 } 743 744 private void notifyInputAddedLocked(UserState userState, String inputId) { 745 if (DEBUG) { 746 Slog.d(TAG, "notifyInputAdded: inputId = " + inputId); 747 } 748 for (ITvInputManagerCallback callback : userState.callbackSet) { 749 try { 750 callback.onInputAdded(inputId); 751 } catch (RemoteException e) { 752 Slog.e(TAG, "Failed to report added input to callback."); 753 } 754 } 755 } 756 757 private void notifyInputRemovedLocked(UserState userState, String inputId) { 758 if (DEBUG) { 759 Slog.d(TAG, "notifyInputRemovedLocked: inputId = " + inputId); 760 } 761 for (ITvInputManagerCallback callback : userState.callbackSet) { 762 try { 763 callback.onInputRemoved(inputId); 764 } catch (RemoteException e) { 765 Slog.e(TAG, "Failed to report removed input to callback."); 766 } 767 } 768 } 769 770 private void notifyInputStateChangedLocked(UserState userState, String inputId, 771 int state, ITvInputManagerCallback targetCallback) { 772 if (DEBUG) { 773 Slog.d(TAG, "notifyInputStateChangedLocked: inputId = " + inputId 774 + "; state = " + state); 775 } 776 if (targetCallback == null) { 777 for (ITvInputManagerCallback callback : userState.callbackSet) { 778 try { 779 callback.onInputStateChanged(inputId, state); 780 } catch (RemoteException e) { 781 Slog.e(TAG, "Failed to report state change to callback."); 782 } 783 } 784 } else { 785 try { 786 targetCallback.onInputStateChanged(inputId, state); 787 } catch (RemoteException e) { 788 Slog.e(TAG, "Failed to report state change to callback."); 789 } 790 } 791 } 792 793 private void setStateLocked(String inputId, int state, int userId) { 794 UserState userState = getUserStateLocked(userId); 795 TvInputState inputState = userState.inputMap.get(inputId); 796 ServiceState serviceState = userState.serviceStateMap.get(inputState.mInfo.getComponent()); 797 int oldState = inputState.mState; 798 inputState.mState = state; 799 if (serviceState != null && serviceState.mService == null 800 && shouldMaintainConnection(serviceState)) { 801 // We don't notify state change while reconnecting. It should remain disconnected. 802 return; 803 } 804 if (oldState != state) { 805 notifyInputStateChangedLocked(userState, inputId, state, null); 806 } 807 } 808 809 private final class BinderService extends ITvInputManager.Stub { 810 @Override 811 public List<TvInputInfo> getTvInputList(int userId) { 812 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 813 Binder.getCallingUid(), userId, "getTvInputList"); 814 final long identity = Binder.clearCallingIdentity(); 815 try { 816 synchronized (mLock) { 817 UserState userState = getUserStateLocked(resolvedUserId); 818 List<TvInputInfo> inputList = new ArrayList<TvInputInfo>(); 819 for (TvInputState state : userState.inputMap.values()) { 820 inputList.add(state.mInfo); 821 } 822 return inputList; 823 } 824 } finally { 825 Binder.restoreCallingIdentity(identity); 826 } 827 } 828 829 @Override 830 public TvInputInfo getTvInputInfo(String inputId, int userId) { 831 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 832 Binder.getCallingUid(), userId, "getTvInputInfo"); 833 final long identity = Binder.clearCallingIdentity(); 834 try { 835 synchronized (mLock) { 836 UserState userState = getUserStateLocked(resolvedUserId); 837 TvInputState state = userState.inputMap.get(inputId); 838 return state == null ? null : state.mInfo; 839 } 840 } finally { 841 Binder.restoreCallingIdentity(identity); 842 } 843 } 844 845 @Override 846 public List<Uri> getTvContentRatingSystemXmls(int userId) { 847 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 848 Binder.getCallingUid(), userId, "getTvContentRatingSystemXmls"); 849 final long identity = Binder.clearCallingIdentity(); 850 try { 851 synchronized (mLock) { 852 UserState userState = getUserStateLocked(resolvedUserId); 853 List<Uri> ratingSystemXmlUriList = new ArrayList<Uri>(); 854 ratingSystemXmlUriList.addAll(userState.ratingSystemXmlUriSet); 855 return ratingSystemXmlUriList; 856 } 857 } finally { 858 Binder.restoreCallingIdentity(identity); 859 } 860 } 861 862 @Override 863 public void registerCallback(final ITvInputManagerCallback callback, int userId) { 864 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 865 Binder.getCallingUid(), userId, "registerCallback"); 866 final long identity = Binder.clearCallingIdentity(); 867 try { 868 synchronized (mLock) { 869 UserState userState = getUserStateLocked(resolvedUserId); 870 userState.callbackSet.add(callback); 871 for (TvInputState state : userState.inputMap.values()) { 872 notifyInputStateChangedLocked(userState, state.mInfo.getId(), 873 state.mState, callback); 874 } 875 } 876 } finally { 877 Binder.restoreCallingIdentity(identity); 878 } 879 } 880 881 @Override 882 public void unregisterCallback(ITvInputManagerCallback callback, int userId) { 883 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 884 Binder.getCallingUid(), userId, "unregisterCallback"); 885 final long identity = Binder.clearCallingIdentity(); 886 try { 887 synchronized (mLock) { 888 UserState userState = getUserStateLocked(resolvedUserId); 889 userState.callbackSet.remove(callback); 890 } 891 } finally { 892 Binder.restoreCallingIdentity(identity); 893 } 894 } 895 896 @Override 897 public boolean isParentalControlsEnabled(int userId) { 898 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 899 Binder.getCallingUid(), userId, "isParentalControlsEnabled"); 900 final long identity = Binder.clearCallingIdentity(); 901 try { 902 synchronized (mLock) { 903 UserState userState = getUserStateLocked(resolvedUserId); 904 return userState.persistentDataStore.isParentalControlsEnabled(); 905 } 906 } finally { 907 Binder.restoreCallingIdentity(identity); 908 } 909 } 910 911 @Override 912 public void setParentalControlsEnabled(boolean enabled, int userId) { 913 ensureParentalControlsPermission(); 914 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 915 Binder.getCallingUid(), userId, "setParentalControlsEnabled"); 916 final long identity = Binder.clearCallingIdentity(); 917 try { 918 synchronized (mLock) { 919 UserState userState = getUserStateLocked(resolvedUserId); 920 userState.persistentDataStore.setParentalControlsEnabled(enabled); 921 } 922 } finally { 923 Binder.restoreCallingIdentity(identity); 924 } 925 } 926 927 @Override 928 public boolean isRatingBlocked(String rating, int userId) { 929 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 930 Binder.getCallingUid(), userId, "isRatingBlocked"); 931 final long identity = Binder.clearCallingIdentity(); 932 try { 933 synchronized (mLock) { 934 UserState userState = getUserStateLocked(resolvedUserId); 935 return userState.persistentDataStore.isRatingBlocked( 936 TvContentRating.unflattenFromString(rating)); 937 } 938 } finally { 939 Binder.restoreCallingIdentity(identity); 940 } 941 } 942 943 @Override 944 public List<String> getBlockedRatings(int userId) { 945 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 946 Binder.getCallingUid(), userId, "getBlockedRatings"); 947 final long identity = Binder.clearCallingIdentity(); 948 try { 949 synchronized (mLock) { 950 UserState userState = getUserStateLocked(resolvedUserId); 951 List<String> ratings = new ArrayList<String>(); 952 for (TvContentRating rating 953 : userState.persistentDataStore.getBlockedRatings()) { 954 ratings.add(rating.flattenToString()); 955 } 956 return ratings; 957 } 958 } finally { 959 Binder.restoreCallingIdentity(identity); 960 } 961 } 962 963 @Override 964 public void addBlockedRating(String rating, int userId) { 965 ensureParentalControlsPermission(); 966 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 967 Binder.getCallingUid(), userId, "addBlockedRating"); 968 final long identity = Binder.clearCallingIdentity(); 969 try { 970 synchronized (mLock) { 971 UserState userState = getUserStateLocked(resolvedUserId); 972 userState.persistentDataStore.addBlockedRating( 973 TvContentRating.unflattenFromString(rating)); 974 } 975 } finally { 976 Binder.restoreCallingIdentity(identity); 977 } 978 } 979 980 @Override 981 public void removeBlockedRating(String rating, int userId) { 982 ensureParentalControlsPermission(); 983 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 984 Binder.getCallingUid(), userId, "removeBlockedRating"); 985 final long identity = Binder.clearCallingIdentity(); 986 try { 987 synchronized (mLock) { 988 UserState userState = getUserStateLocked(resolvedUserId); 989 userState.persistentDataStore.removeBlockedRating( 990 TvContentRating.unflattenFromString(rating)); 991 } 992 } finally { 993 Binder.restoreCallingIdentity(identity); 994 } 995 } 996 997 private void ensureParentalControlsPermission() { 998 if (mContext.checkCallingPermission( 999 android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) 1000 != PackageManager.PERMISSION_GRANTED) { 1001 throw new SecurityException( 1002 "The caller does not have parental controls permission"); 1003 } 1004 } 1005 1006 @Override 1007 public void createSession(final ITvInputClient client, final String inputId, 1008 int seq, int userId) { 1009 final int callingUid = Binder.getCallingUid(); 1010 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1011 userId, "createSession"); 1012 final long identity = Binder.clearCallingIdentity(); 1013 try { 1014 synchronized (mLock) { 1015 UserState userState = getUserStateLocked(resolvedUserId); 1016 TvInputInfo info = userState.inputMap.get(inputId).mInfo; 1017 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 1018 if (serviceState == null) { 1019 serviceState = new ServiceState(info.getComponent(), resolvedUserId); 1020 userState.serviceStateMap.put(info.getComponent(), serviceState); 1021 } 1022 // Send a null token immediately while reconnecting. 1023 if (serviceState.mReconnecting == true) { 1024 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 1025 return; 1026 } 1027 1028 // Create a new session token and a session state. 1029 IBinder sessionToken = new Binder(); 1030 SessionState sessionState = new SessionState(sessionToken, info, client, 1031 seq, callingUid, resolvedUserId); 1032 1033 // Add them to the global session state map of the current user. 1034 userState.sessionStateMap.put(sessionToken, sessionState); 1035 1036 // Also, add them to the session state map of the current service. 1037 serviceState.mSessionTokens.add(sessionToken); 1038 1039 if (serviceState.mService != null) { 1040 createSessionInternalLocked(serviceState.mService, sessionToken, 1041 resolvedUserId); 1042 } else { 1043 updateServiceConnectionLocked(info.getComponent(), resolvedUserId); 1044 } 1045 } 1046 } finally { 1047 Binder.restoreCallingIdentity(identity); 1048 } 1049 } 1050 1051 @Override 1052 public void releaseSession(IBinder sessionToken, int userId) { 1053 final int callingUid = Binder.getCallingUid(); 1054 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1055 userId, "releaseSession"); 1056 final long identity = Binder.clearCallingIdentity(); 1057 try { 1058 synchronized (mLock) { 1059 releaseSessionLocked(sessionToken, callingUid, resolvedUserId); 1060 } 1061 } finally { 1062 Binder.restoreCallingIdentity(identity); 1063 } 1064 } 1065 1066 @Override 1067 public void setMainSession(IBinder sessionToken, int userId) { 1068 final int callingUid = Binder.getCallingUid(); 1069 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1070 userId, "setMainSession"); 1071 final long identity = Binder.clearCallingIdentity(); 1072 try { 1073 synchronized (mLock) { 1074 UserState userState = getUserStateLocked(resolvedUserId); 1075 if (userState.mainSessionToken == sessionToken) { 1076 return; 1077 } 1078 1079 SessionState newMainSessionState = getSessionStateLocked( 1080 sessionToken, callingUid, resolvedUserId); 1081 if (newMainSessionState.mHardwareSessionToken != null) { 1082 newMainSessionState = getSessionStateLocked( 1083 newMainSessionState.mHardwareSessionToken, 1084 Process.SYSTEM_UID, resolvedUserId); 1085 } 1086 ServiceState newMainServiceState = getServiceStateLocked( 1087 newMainSessionState.mInfo.getComponent(), resolvedUserId); 1088 ITvInputSession newMainSession = getSessionLocked(newMainSessionState); 1089 1090 ServiceState oldMainServiceState = null; 1091 ITvInputSession oldMainSession = null; 1092 if (userState.mainSessionToken != null) { 1093 SessionState oldMainSessionState = getSessionStateLocked( 1094 userState.mainSessionToken, Process.SYSTEM_UID, resolvedUserId); 1095 if (oldMainSessionState.mHardwareSessionToken != null) { 1096 oldMainSessionState = getSessionStateLocked( 1097 oldMainSessionState.mHardwareSessionToken, 1098 Process.SYSTEM_UID, resolvedUserId); 1099 } 1100 oldMainServiceState = getServiceStateLocked( 1101 oldMainSessionState.mInfo.getComponent(), resolvedUserId); 1102 oldMainSession = getSessionLocked(oldMainSessionState); 1103 } 1104 1105 userState.mainSessionToken = sessionToken; 1106 1107 // Inform the new main session first. 1108 // See {@link TvInputService#onSetMainSession}. 1109 if (newMainServiceState.mIsHardware) { 1110 try { 1111 newMainSession.setMainSession(true); 1112 } catch (RemoteException e) { 1113 Slog.e(TAG, "error in setMainSession", e); 1114 } 1115 } 1116 if (oldMainSession != null && oldMainServiceState.mIsHardware) { 1117 try { 1118 oldMainSession.setMainSession(false); 1119 } catch (RemoteException e) { 1120 Slog.e(TAG, "error in setMainSession", e); 1121 } 1122 } 1123 } 1124 } finally { 1125 Binder.restoreCallingIdentity(identity); 1126 } 1127 } 1128 1129 @Override 1130 public void setSurface(IBinder sessionToken, Surface surface, int userId) { 1131 final int callingUid = Binder.getCallingUid(); 1132 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1133 userId, "setSurface"); 1134 final long identity = Binder.clearCallingIdentity(); 1135 try { 1136 synchronized (mLock) { 1137 try { 1138 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1139 resolvedUserId); 1140 if (sessionState.mHardwareSessionToken == null) { 1141 getSessionLocked(sessionState).setSurface(surface); 1142 } else { 1143 getSessionLocked(sessionState.mHardwareSessionToken, 1144 Process.SYSTEM_UID, resolvedUserId).setSurface(surface); 1145 } 1146 } catch (RemoteException e) { 1147 Slog.e(TAG, "error in setSurface", e); 1148 } 1149 } 1150 } finally { 1151 if (surface != null) { 1152 // surface is not used in TvInputManagerService. 1153 surface.release(); 1154 } 1155 Binder.restoreCallingIdentity(identity); 1156 } 1157 } 1158 1159 @Override 1160 public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width, 1161 int height, int userId) { 1162 final int callingUid = Binder.getCallingUid(); 1163 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1164 userId, "dispatchSurfaceChanged"); 1165 final long identity = Binder.clearCallingIdentity(); 1166 try { 1167 synchronized (mLock) { 1168 try { 1169 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1170 resolvedUserId); 1171 getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, height); 1172 if (sessionState.mHardwareSessionToken != null) { 1173 getSessionLocked(sessionState.mHardwareSessionToken, Process.SYSTEM_UID, 1174 resolvedUserId).dispatchSurfaceChanged(format, width, height); 1175 } 1176 } catch (RemoteException e) { 1177 Slog.e(TAG, "error in dispatchSurfaceChanged", e); 1178 } 1179 } 1180 } finally { 1181 Binder.restoreCallingIdentity(identity); 1182 } 1183 } 1184 1185 @Override 1186 public void setVolume(IBinder sessionToken, float volume, int userId) { 1187 final float REMOTE_VOLUME_ON = 1.0f; 1188 final float REMOTE_VOLUME_OFF = 0f; 1189 final int callingUid = Binder.getCallingUid(); 1190 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1191 userId, "setVolume"); 1192 final long identity = Binder.clearCallingIdentity(); 1193 try { 1194 synchronized (mLock) { 1195 try { 1196 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1197 resolvedUserId); 1198 getSessionLocked(sessionState).setVolume(volume); 1199 if (sessionState.mHardwareSessionToken != null) { 1200 // Here, we let the hardware session know only whether volume is on or 1201 // off to prevent that the volume is controlled in the both side. 1202 getSessionLocked(sessionState.mHardwareSessionToken, 1203 Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f) 1204 ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF); 1205 } 1206 } catch (RemoteException e) { 1207 Slog.e(TAG, "error in setVolume", e); 1208 } 1209 } 1210 } finally { 1211 Binder.restoreCallingIdentity(identity); 1212 } 1213 } 1214 1215 @Override 1216 public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) { 1217 final int callingUid = Binder.getCallingUid(); 1218 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1219 userId, "tune"); 1220 final long identity = Binder.clearCallingIdentity(); 1221 try { 1222 synchronized (mLock) { 1223 try { 1224 getSessionLocked(sessionToken, callingUid, resolvedUserId).tune( 1225 channelUri, params); 1226 if (TvContract.isChannelUriForPassthroughTvInput(channelUri)) { 1227 // Do not log the watch history for passthrough inputs. 1228 return; 1229 } 1230 long currentTime = System.currentTimeMillis(); 1231 long channelId = ContentUris.parseId(channelUri); 1232 1233 // Close the open log entry first, if any. 1234 UserState userState = getUserStateLocked(resolvedUserId); 1235 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 1236 if (sessionState.mLogUri != null) { 1237 SomeArgs args = SomeArgs.obtain(); 1238 args.arg1 = sessionState.mLogUri; 1239 args.arg2 = currentTime; 1240 mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args) 1241 .sendToTarget(); 1242 } 1243 1244 // Create a log entry and fill it later. 1245 String packageName = sessionState.mInfo.getServiceInfo().packageName; 1246 ContentValues values = new ContentValues(); 1247 values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName); 1248 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 1249 currentTime); 1250 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0); 1251 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 1252 if (params != null) { 1253 values.put(TvContract.WatchedPrograms.COLUMN_TUNE_PARAMS, 1254 encodeTuneParams(params)); 1255 } 1256 1257 sessionState.mLogUri = mContentResolver.insert( 1258 TvContract.WatchedPrograms.CONTENT_URI, values); 1259 SomeArgs args = SomeArgs.obtain(); 1260 args.arg1 = sessionState.mLogUri; 1261 args.arg2 = ContentUris.parseId(channelUri); 1262 args.arg3 = currentTime; 1263 args.arg4 = sessionState; 1264 mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget(); 1265 } catch (RemoteException e) { 1266 Slog.e(TAG, "error in tune", e); 1267 return; 1268 } 1269 } 1270 } finally { 1271 Binder.restoreCallingIdentity(identity); 1272 } 1273 } 1274 1275 @Override 1276 public void requestUnblockContent( 1277 IBinder sessionToken, String unblockedRating, int userId) { 1278 final int callingUid = Binder.getCallingUid(); 1279 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1280 userId, "unblockContent"); 1281 final long identity = Binder.clearCallingIdentity(); 1282 try { 1283 synchronized (mLock) { 1284 try { 1285 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1286 .requestUnblockContent(unblockedRating); 1287 } catch (RemoteException e) { 1288 Slog.e(TAG, "error in unblockContent", e); 1289 } 1290 } 1291 } finally { 1292 Binder.restoreCallingIdentity(identity); 1293 } 1294 } 1295 1296 @Override 1297 public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) { 1298 final int callingUid = Binder.getCallingUid(); 1299 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1300 userId, "setCaptionEnabled"); 1301 final long identity = Binder.clearCallingIdentity(); 1302 try { 1303 synchronized (mLock) { 1304 try { 1305 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1306 .setCaptionEnabled(enabled); 1307 } catch (RemoteException e) { 1308 Slog.e(TAG, "error in setCaptionEnabled", e); 1309 } 1310 } 1311 } finally { 1312 Binder.restoreCallingIdentity(identity); 1313 } 1314 } 1315 1316 @Override 1317 public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) { 1318 final int callingUid = Binder.getCallingUid(); 1319 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1320 userId, "selectTrack"); 1321 final long identity = Binder.clearCallingIdentity(); 1322 try { 1323 synchronized (mLock) { 1324 try { 1325 getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack( 1326 type, trackId); 1327 } catch (RemoteException e) { 1328 Slog.e(TAG, "error in selectTrack", e); 1329 } 1330 } 1331 } finally { 1332 Binder.restoreCallingIdentity(identity); 1333 } 1334 } 1335 1336 @Override 1337 public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data, 1338 int userId) { 1339 final int callingUid = Binder.getCallingUid(); 1340 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1341 userId, "sendAppPrivateCommand"); 1342 final long identity = Binder.clearCallingIdentity(); 1343 try { 1344 synchronized (mLock) { 1345 try { 1346 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1347 .appPrivateCommand(command, data); 1348 } catch (RemoteException e) { 1349 Slog.e(TAG, "error in sendAppPrivateCommand", e); 1350 } 1351 } 1352 } finally { 1353 Binder.restoreCallingIdentity(identity); 1354 } 1355 } 1356 1357 @Override 1358 public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame, 1359 int userId) { 1360 final int callingUid = Binder.getCallingUid(); 1361 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1362 userId, "createOverlayView"); 1363 final long identity = Binder.clearCallingIdentity(); 1364 try { 1365 synchronized (mLock) { 1366 try { 1367 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1368 .createOverlayView(windowToken, frame); 1369 } catch (RemoteException e) { 1370 Slog.e(TAG, "error in createOverlayView", e); 1371 } 1372 } 1373 } finally { 1374 Binder.restoreCallingIdentity(identity); 1375 } 1376 } 1377 1378 @Override 1379 public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) { 1380 final int callingUid = Binder.getCallingUid(); 1381 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1382 userId, "relayoutOverlayView"); 1383 final long identity = Binder.clearCallingIdentity(); 1384 try { 1385 synchronized (mLock) { 1386 try { 1387 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1388 .relayoutOverlayView(frame); 1389 } catch (RemoteException e) { 1390 Slog.e(TAG, "error in relayoutOverlayView", e); 1391 } 1392 } 1393 } finally { 1394 Binder.restoreCallingIdentity(identity); 1395 } 1396 } 1397 1398 @Override 1399 public void removeOverlayView(IBinder sessionToken, int userId) { 1400 final int callingUid = Binder.getCallingUid(); 1401 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1402 userId, "removeOverlayView"); 1403 final long identity = Binder.clearCallingIdentity(); 1404 try { 1405 synchronized (mLock) { 1406 try { 1407 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1408 .removeOverlayView(); 1409 } catch (RemoteException e) { 1410 Slog.e(TAG, "error in removeOverlayView", e); 1411 } 1412 } 1413 } finally { 1414 Binder.restoreCallingIdentity(identity); 1415 } 1416 } 1417 1418 @Override 1419 public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { 1420 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1421 != PackageManager.PERMISSION_GRANTED) { 1422 return null; 1423 } 1424 1425 final long identity = Binder.clearCallingIdentity(); 1426 try { 1427 return mTvInputHardwareManager.getHardwareList(); 1428 } finally { 1429 Binder.restoreCallingIdentity(identity); 1430 } 1431 } 1432 1433 @Override 1434 public ITvInputHardware acquireTvInputHardware(int deviceId, 1435 ITvInputHardwareCallback callback, TvInputInfo info, int userId) 1436 throws RemoteException { 1437 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1438 != PackageManager.PERMISSION_GRANTED) { 1439 return null; 1440 } 1441 1442 final long identity = Binder.clearCallingIdentity(); 1443 final int callingUid = Binder.getCallingUid(); 1444 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1445 userId, "acquireTvInputHardware"); 1446 try { 1447 return mTvInputHardwareManager.acquireHardware( 1448 deviceId, callback, info, callingUid, resolvedUserId); 1449 } finally { 1450 Binder.restoreCallingIdentity(identity); 1451 } 1452 } 1453 1454 @Override 1455 public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) 1456 throws RemoteException { 1457 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1458 != PackageManager.PERMISSION_GRANTED) { 1459 return; 1460 } 1461 1462 final long identity = Binder.clearCallingIdentity(); 1463 final int callingUid = Binder.getCallingUid(); 1464 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1465 userId, "releaseTvInputHardware"); 1466 try { 1467 mTvInputHardwareManager.releaseHardware( 1468 deviceId, hardware, callingUid, resolvedUserId); 1469 } finally { 1470 Binder.restoreCallingIdentity(identity); 1471 } 1472 } 1473 1474 @Override 1475 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId) 1476 throws RemoteException { 1477 if (mContext.checkCallingPermission( 1478 android.Manifest.permission.CAPTURE_TV_INPUT) 1479 != PackageManager.PERMISSION_GRANTED) { 1480 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 1481 } 1482 1483 final long identity = Binder.clearCallingIdentity(); 1484 final int callingUid = Binder.getCallingUid(); 1485 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1486 userId, "getAvailableTvStreamConfigList"); 1487 try { 1488 return mTvInputHardwareManager.getAvailableTvStreamConfigList( 1489 inputId, callingUid, resolvedUserId); 1490 } finally { 1491 Binder.restoreCallingIdentity(identity); 1492 } 1493 } 1494 1495 @Override 1496 public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config, 1497 int userId) 1498 throws RemoteException { 1499 if (mContext.checkCallingPermission( 1500 android.Manifest.permission.CAPTURE_TV_INPUT) 1501 != PackageManager.PERMISSION_GRANTED) { 1502 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 1503 } 1504 1505 final long identity = Binder.clearCallingIdentity(); 1506 final int callingUid = Binder.getCallingUid(); 1507 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1508 userId, "captureFrame"); 1509 try { 1510 String hardwareInputId = null; 1511 synchronized (mLock) { 1512 UserState userState = getUserStateLocked(resolvedUserId); 1513 if (userState.inputMap.get(inputId) == null) { 1514 Slog.e(TAG, "Input not found for " + inputId); 1515 return false; 1516 } 1517 for (SessionState sessionState : userState.sessionStateMap.values()) { 1518 if (sessionState.mInfo.getId().equals(inputId) 1519 && sessionState.mHardwareSessionToken != null) { 1520 hardwareInputId = userState.sessionStateMap.get( 1521 sessionState.mHardwareSessionToken).mInfo.getId(); 1522 break; 1523 } 1524 } 1525 } 1526 return mTvInputHardwareManager.captureFrame( 1527 (hardwareInputId != null) ? hardwareInputId : inputId, 1528 surface, config, callingUid, resolvedUserId); 1529 } finally { 1530 Binder.restoreCallingIdentity(identity); 1531 } 1532 } 1533 1534 @Override 1535 public boolean isSingleSessionActive(int userId) throws RemoteException { 1536 final long identity = Binder.clearCallingIdentity(); 1537 final int callingUid = Binder.getCallingUid(); 1538 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1539 userId, "isSingleSessionActive"); 1540 try { 1541 synchronized (mLock) { 1542 UserState userState = getUserStateLocked(resolvedUserId); 1543 if (userState.sessionStateMap.size() == 1) { 1544 return true; 1545 } 1546 else if (userState.sessionStateMap.size() == 2) { 1547 SessionState[] sessionStates = userState.sessionStateMap.values().toArray( 1548 new SessionState[0]); 1549 // Check if there is a wrapper input. 1550 if (sessionStates[0].mHardwareSessionToken != null 1551 || sessionStates[1].mHardwareSessionToken != null) { 1552 return true; 1553 } 1554 } 1555 return false; 1556 } 1557 } finally { 1558 Binder.restoreCallingIdentity(identity); 1559 } 1560 } 1561 1562 @Override 1563 @SuppressWarnings("resource") 1564 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 1565 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 1566 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1567 != PackageManager.PERMISSION_GRANTED) { 1568 pw.println("Permission Denial: can't dump TvInputManager from pid=" 1569 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 1570 return; 1571 } 1572 1573 synchronized (mLock) { 1574 pw.println("User Ids (Current user: " + mCurrentUserId + "):"); 1575 pw.increaseIndent(); 1576 for (int i = 0; i < mUserStates.size(); i++) { 1577 int userId = mUserStates.keyAt(i); 1578 pw.println(Integer.valueOf(userId)); 1579 } 1580 pw.decreaseIndent(); 1581 1582 for (int i = 0; i < mUserStates.size(); i++) { 1583 int userId = mUserStates.keyAt(i); 1584 UserState userState = getUserStateLocked(userId); 1585 pw.println("UserState (" + userId + "):"); 1586 pw.increaseIndent(); 1587 1588 pw.println("inputMap: inputId -> TvInputState"); 1589 pw.increaseIndent(); 1590 for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) { 1591 pw.println(entry.getKey() + ": " + entry.getValue()); 1592 } 1593 pw.decreaseIndent(); 1594 1595 pw.println("packageSet:"); 1596 pw.increaseIndent(); 1597 for (String packageName : userState.packageSet) { 1598 pw.println(packageName); 1599 } 1600 pw.decreaseIndent(); 1601 1602 pw.println("clientStateMap: ITvInputClient -> ClientState"); 1603 pw.increaseIndent(); 1604 for (Map.Entry<IBinder, ClientState> entry : 1605 userState.clientStateMap.entrySet()) { 1606 ClientState client = entry.getValue(); 1607 pw.println(entry.getKey() + ": " + client); 1608 1609 pw.increaseIndent(); 1610 1611 pw.println("mSessionTokens:"); 1612 pw.increaseIndent(); 1613 for (IBinder token : client.mSessionTokens) { 1614 pw.println("" + token); 1615 } 1616 pw.decreaseIndent(); 1617 1618 pw.println("mClientTokens: " + client.mClientToken); 1619 pw.println("mUserId: " + client.mUserId); 1620 1621 pw.decreaseIndent(); 1622 } 1623 pw.decreaseIndent(); 1624 1625 pw.println("serviceStateMap: ComponentName -> ServiceState"); 1626 pw.increaseIndent(); 1627 for (Map.Entry<ComponentName, ServiceState> entry : 1628 userState.serviceStateMap.entrySet()) { 1629 ServiceState service = entry.getValue(); 1630 pw.println(entry.getKey() + ": " + service); 1631 1632 pw.increaseIndent(); 1633 1634 pw.println("mClientTokens:"); 1635 pw.increaseIndent(); 1636 for (IBinder token : service.mClientTokens) { 1637 pw.println("" + token); 1638 } 1639 pw.decreaseIndent(); 1640 1641 pw.println("mSessionTokens:"); 1642 pw.increaseIndent(); 1643 for (IBinder token : service.mSessionTokens) { 1644 pw.println("" + token); 1645 } 1646 pw.decreaseIndent(); 1647 1648 pw.println("mService: " + service.mService); 1649 pw.println("mCallback: " + service.mCallback); 1650 pw.println("mBound: " + service.mBound); 1651 pw.println("mReconnecting: " + service.mReconnecting); 1652 1653 pw.decreaseIndent(); 1654 } 1655 pw.decreaseIndent(); 1656 1657 pw.println("sessionStateMap: ITvInputSession -> SessionState"); 1658 pw.increaseIndent(); 1659 for (Map.Entry<IBinder, SessionState> entry : 1660 userState.sessionStateMap.entrySet()) { 1661 SessionState session = entry.getValue(); 1662 pw.println(entry.getKey() + ": " + session); 1663 1664 pw.increaseIndent(); 1665 pw.println("mInfo: " + session.mInfo); 1666 pw.println("mClient: " + session.mClient); 1667 pw.println("mSeq: " + session.mSeq); 1668 pw.println("mCallingUid: " + session.mCallingUid); 1669 pw.println("mUserId: " + session.mUserId); 1670 pw.println("mSessionToken: " + session.mSessionToken); 1671 pw.println("mSession: " + session.mSession); 1672 pw.println("mLogUri: " + session.mLogUri); 1673 pw.println("mHardwareSessionToken: " + session.mHardwareSessionToken); 1674 pw.decreaseIndent(); 1675 } 1676 pw.decreaseIndent(); 1677 1678 pw.println("callbackSet:"); 1679 pw.increaseIndent(); 1680 for (ITvInputManagerCallback callback : userState.callbackSet) { 1681 pw.println(callback.toString()); 1682 } 1683 pw.decreaseIndent(); 1684 1685 pw.println("mainSessionToken: " + userState.mainSessionToken); 1686 pw.decreaseIndent(); 1687 } 1688 } 1689 } 1690 1691 private String encodeTuneParams(Bundle tuneParams) { 1692 StringBuilder builder = new StringBuilder(); 1693 Set<String> keySet = tuneParams.keySet(); 1694 Iterator<String> it = keySet.iterator(); 1695 while (it.hasNext()) { 1696 String key = it.next(); 1697 Object value = tuneParams.get(key); 1698 if (value == null) { 1699 continue; 1700 } 1701 builder.append(replaceEscapeCharacters(key)); 1702 builder.append("="); 1703 builder.append(replaceEscapeCharacters(value.toString())); 1704 if (it.hasNext()) { 1705 builder.append(", "); 1706 } 1707 } 1708 return builder.toString(); 1709 } 1710 1711 private String replaceEscapeCharacters(String src) { 1712 final char ESCAPE_CHARACTER = '%'; 1713 final String ENCODING_TARGET_CHARACTERS = "%=,"; 1714 StringBuilder builder = new StringBuilder(); 1715 for (char ch : src.toCharArray()) { 1716 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) { 1717 builder.append(ESCAPE_CHARACTER); 1718 } 1719 builder.append(ch); 1720 } 1721 return builder.toString(); 1722 } 1723 } 1724 1725 private static final class TvInputState { 1726 // A TvInputInfo object which represents the TV input. 1727 private TvInputInfo mInfo; 1728 1729 // The state of TV input. Connected by default. 1730 private int mState = INPUT_STATE_CONNECTED; 1731 1732 @Override 1733 public String toString() { 1734 return "mInfo: " + mInfo + "; mState: " + mState; 1735 } 1736 } 1737 1738 private static final class UserState { 1739 // A mapping from the TV input id to its TvInputState. 1740 private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>(); 1741 1742 // A set of all TV input packages. 1743 private final Set<String> packageSet = new HashSet<String>(); 1744 1745 // A set of all TV content rating system xml uris. 1746 private final Set<Uri> ratingSystemXmlUriSet = new HashSet<Uri>(); 1747 1748 // A mapping from the token of a client to its state. 1749 private final Map<IBinder, ClientState> clientStateMap = 1750 new HashMap<IBinder, ClientState>(); 1751 1752 // A mapping from the name of a TV input service to its state. 1753 private final Map<ComponentName, ServiceState> serviceStateMap = 1754 new HashMap<ComponentName, ServiceState>(); 1755 1756 // A mapping from the token of a TV input session to its state. 1757 private final Map<IBinder, SessionState> sessionStateMap = 1758 new HashMap<IBinder, SessionState>(); 1759 1760 // A set of callbacks. 1761 private final Set<ITvInputManagerCallback> callbackSet = 1762 new HashSet<ITvInputManagerCallback>(); 1763 1764 // The token of a "main" TV input session. 1765 private IBinder mainSessionToken = null; 1766 1767 // Persistent data store for all internal settings maintained by the TV input manager 1768 // service. 1769 private final PersistentDataStore persistentDataStore; 1770 1771 private UserState(Context context, int userId) { 1772 persistentDataStore = new PersistentDataStore(context, userId); 1773 } 1774 } 1775 1776 private final class ClientState implements IBinder.DeathRecipient { 1777 private final List<IBinder> mSessionTokens = new ArrayList<IBinder>(); 1778 1779 private IBinder mClientToken; 1780 private final int mUserId; 1781 1782 ClientState(IBinder clientToken, int userId) { 1783 mClientToken = clientToken; 1784 mUserId = userId; 1785 } 1786 1787 public boolean isEmpty() { 1788 return mSessionTokens.isEmpty(); 1789 } 1790 1791 @Override 1792 public void binderDied() { 1793 synchronized (mLock) { 1794 UserState userState = getUserStateLocked(mUserId); 1795 // DO NOT remove the client state of clientStateMap in this method. It will be 1796 // removed in releaseSessionLocked(). 1797 ClientState clientState = userState.clientStateMap.get(mClientToken); 1798 if (clientState != null) { 1799 while (clientState.mSessionTokens.size() > 0) { 1800 releaseSessionLocked( 1801 clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId); 1802 } 1803 } 1804 mClientToken = null; 1805 } 1806 } 1807 } 1808 1809 private final class ServiceState { 1810 private final List<IBinder> mClientTokens = new ArrayList<IBinder>(); 1811 private final List<IBinder> mSessionTokens = new ArrayList<IBinder>(); 1812 private final ServiceConnection mConnection; 1813 private final ComponentName mComponent; 1814 private final boolean mIsHardware; 1815 private final List<TvInputInfo> mInputList = new ArrayList<TvInputInfo>(); 1816 1817 private ITvInputService mService; 1818 private ServiceCallback mCallback; 1819 private boolean mBound; 1820 private boolean mReconnecting; 1821 1822 private ServiceState(ComponentName component, int userId) { 1823 mComponent = component; 1824 mConnection = new InputServiceConnection(component, userId); 1825 mIsHardware = hasHardwarePermission(mContext.getPackageManager(), mComponent); 1826 } 1827 } 1828 1829 private final class SessionState implements IBinder.DeathRecipient { 1830 private final TvInputInfo mInfo; 1831 private final ITvInputClient mClient; 1832 private final int mSeq; 1833 private final int mCallingUid; 1834 private final int mUserId; 1835 private final IBinder mSessionToken; 1836 private ITvInputSession mSession; 1837 private Uri mLogUri; 1838 // Not null if this session represents an external device connected to a hardware TV input. 1839 private IBinder mHardwareSessionToken; 1840 1841 private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client, 1842 int seq, int callingUid, int userId) { 1843 mSessionToken = sessionToken; 1844 mInfo = info; 1845 mClient = client; 1846 mSeq = seq; 1847 mCallingUid = callingUid; 1848 mUserId = userId; 1849 } 1850 1851 @Override 1852 public void binderDied() { 1853 synchronized (mLock) { 1854 mSession = null; 1855 if (mClient != null) { 1856 try { 1857 mClient.onSessionReleased(mSeq); 1858 } catch(RemoteException e) { 1859 Slog.e(TAG, "error in onSessionReleased", e); 1860 } 1861 } 1862 // If there are any other sessions based on this session, they should be released. 1863 UserState userState = getUserStateLocked(mUserId); 1864 for (SessionState sessionState : userState.sessionStateMap.values()) { 1865 if (mSession != null && mSession == sessionState.mHardwareSessionToken) { 1866 try { 1867 sessionState.mSession.release(); 1868 } catch (RemoteException e) { 1869 Slog.e(TAG, "error in release", e); 1870 } 1871 try { 1872 sessionState.mClient.onSessionReleased(sessionState.mSeq); 1873 } catch (RemoteException e) { 1874 Slog.e(TAG, "error in onSessionReleased", e); 1875 } 1876 } 1877 } 1878 removeSessionStateLocked(mSessionToken, mUserId); 1879 } 1880 } 1881 } 1882 1883 private final class InputServiceConnection implements ServiceConnection { 1884 private final ComponentName mComponent; 1885 private final int mUserId; 1886 1887 private InputServiceConnection(ComponentName component, int userId) { 1888 mComponent = component; 1889 mUserId = userId; 1890 } 1891 1892 @Override 1893 public void onServiceConnected(ComponentName component, IBinder service) { 1894 if (DEBUG) { 1895 Slog.d(TAG, "onServiceConnected(component=" + component + ")"); 1896 } 1897 synchronized (mLock) { 1898 UserState userState = getUserStateLocked(mUserId); 1899 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 1900 serviceState.mService = ITvInputService.Stub.asInterface(service); 1901 1902 // Register a callback, if we need to. 1903 if (serviceState.mIsHardware && serviceState.mCallback == null) { 1904 serviceState.mCallback = new ServiceCallback(mComponent, mUserId); 1905 try { 1906 serviceState.mService.registerCallback(serviceState.mCallback); 1907 } catch (RemoteException e) { 1908 Slog.e(TAG, "error in registerCallback", e); 1909 } 1910 } 1911 1912 // And create sessions, if any. 1913 for (IBinder sessionToken : serviceState.mSessionTokens) { 1914 createSessionInternalLocked(serviceState.mService, sessionToken, mUserId); 1915 } 1916 1917 for (TvInputState inputState : userState.inputMap.values()) { 1918 if (inputState.mInfo.getComponent().equals(component) 1919 && inputState.mState != INPUT_STATE_DISCONNECTED) { 1920 notifyInputStateChangedLocked(userState, inputState.mInfo.getId(), 1921 inputState.mState, null); 1922 } 1923 } 1924 1925 if (serviceState.mIsHardware) { 1926 List<TvInputHardwareInfo> hardwareInfoList = 1927 mTvInputHardwareManager.getHardwareList(); 1928 for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) { 1929 try { 1930 serviceState.mService.notifyHardwareAdded(hardwareInfo); 1931 } catch (RemoteException e) { 1932 Slog.e(TAG, "error in notifyHardwareAdded", e); 1933 } 1934 } 1935 1936 List<HdmiDeviceInfo> deviceInfoList = 1937 mTvInputHardwareManager.getHdmiDeviceList(); 1938 for (HdmiDeviceInfo deviceInfo : deviceInfoList) { 1939 try { 1940 serviceState.mService.notifyHdmiDeviceAdded(deviceInfo); 1941 } catch (RemoteException e) { 1942 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 1943 } 1944 } 1945 } 1946 } 1947 } 1948 1949 @Override 1950 public void onServiceDisconnected(ComponentName component) { 1951 if (DEBUG) { 1952 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")"); 1953 } 1954 if (!mComponent.equals(component)) { 1955 throw new IllegalArgumentException("Mismatched ComponentName: " 1956 + mComponent + " (expected), " + component + " (actual)."); 1957 } 1958 synchronized (mLock) { 1959 UserState userState = getUserStateLocked(mUserId); 1960 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 1961 if (serviceState != null) { 1962 serviceState.mReconnecting = true; 1963 serviceState.mBound = false; 1964 serviceState.mService = null; 1965 serviceState.mCallback = null; 1966 1967 // Send null tokens for not finishing create session events. 1968 for (IBinder sessionToken : serviceState.mSessionTokens) { 1969 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 1970 if (sessionState.mSession == null) { 1971 removeSessionStateLocked(sessionToken, sessionState.mUserId); 1972 sendSessionTokenToClientLocked(sessionState.mClient, 1973 sessionState.mInfo.getId(), null, null, sessionState.mSeq); 1974 } 1975 } 1976 1977 for (TvInputState inputState : userState.inputMap.values()) { 1978 if (inputState.mInfo.getComponent().equals(component)) { 1979 notifyInputStateChangedLocked(userState, inputState.mInfo.getId(), 1980 INPUT_STATE_DISCONNECTED, null); 1981 } 1982 } 1983 updateServiceConnectionLocked(mComponent, mUserId); 1984 } 1985 } 1986 } 1987 } 1988 1989 private final class ServiceCallback extends ITvInputServiceCallback.Stub { 1990 private final ComponentName mComponent; 1991 private final int mUserId; 1992 1993 ServiceCallback(ComponentName component, int userId) { 1994 mComponent = component; 1995 mUserId = userId; 1996 } 1997 1998 private void ensureHardwarePermission() { 1999 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 2000 != PackageManager.PERMISSION_GRANTED) { 2001 throw new SecurityException("The caller does not have hardware permission"); 2002 } 2003 } 2004 2005 private void ensureValidInput(TvInputInfo inputInfo) { 2006 if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) { 2007 throw new IllegalArgumentException("Invalid TvInputInfo"); 2008 } 2009 } 2010 2011 private void addTvInputLocked(TvInputInfo inputInfo) { 2012 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 2013 serviceState.mInputList.add(inputInfo); 2014 buildTvInputListLocked(mUserId); 2015 } 2016 2017 @Override 2018 public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) { 2019 ensureHardwarePermission(); 2020 ensureValidInput(inputInfo); 2021 synchronized (mLock) { 2022 mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo); 2023 addTvInputLocked(inputInfo); 2024 } 2025 } 2026 2027 @Override 2028 public void addHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) { 2029 ensureHardwarePermission(); 2030 ensureValidInput(inputInfo); 2031 synchronized (mLock) { 2032 mTvInputHardwareManager.addHdmiTvInput(logicalAddress, inputInfo); 2033 addTvInputLocked(inputInfo); 2034 } 2035 } 2036 2037 @Override 2038 public void removeTvInput(String inputId) { 2039 ensureHardwarePermission(); 2040 synchronized (mLock) { 2041 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 2042 boolean removed = false; 2043 for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator(); 2044 it.hasNext(); ) { 2045 if (it.next().getId().equals(inputId)) { 2046 it.remove(); 2047 removed = true; 2048 break; 2049 } 2050 } 2051 if (removed) { 2052 buildTvInputListLocked(mUserId); 2053 mTvInputHardwareManager.removeTvInput(inputId); 2054 } else { 2055 Slog.e(TAG, "TvInputInfo with inputId=" + inputId + " not found."); 2056 } 2057 } 2058 } 2059 } 2060 2061 private final class LogHandler extends Handler { 2062 private static final int MSG_OPEN_ENTRY = 1; 2063 private static final int MSG_UPDATE_ENTRY = 2; 2064 private static final int MSG_CLOSE_ENTRY = 3; 2065 2066 public LogHandler(Looper looper) { 2067 super(looper); 2068 } 2069 2070 @Override 2071 public void handleMessage(Message msg) { 2072 switch (msg.what) { 2073 case MSG_OPEN_ENTRY: { 2074 SomeArgs args = (SomeArgs) msg.obj; 2075 Uri uri = (Uri) args.arg1; 2076 long channelId = (long) args.arg2; 2077 long time = (long) args.arg3; 2078 SessionState sessionState = (SessionState) args.arg4; 2079 onOpenEntry(uri, channelId, time, sessionState); 2080 args.recycle(); 2081 return; 2082 } 2083 case MSG_UPDATE_ENTRY: { 2084 SomeArgs args = (SomeArgs) msg.obj; 2085 Uri uri = (Uri) args.arg1; 2086 long channelId = (long) args.arg2; 2087 long time = (long) args.arg3; 2088 SessionState sessionState = (SessionState) args.arg4; 2089 onUpdateEntry(uri, channelId, time, sessionState); 2090 args.recycle(); 2091 return; 2092 } 2093 case MSG_CLOSE_ENTRY: { 2094 SomeArgs args = (SomeArgs) msg.obj; 2095 Uri uri = (Uri) args.arg1; 2096 long time = (long) args.arg2; 2097 onCloseEntry(uri, time); 2098 args.recycle(); 2099 return; 2100 } 2101 default: { 2102 Slog.w(TAG, "Unhandled message code: " + msg.what); 2103 return; 2104 } 2105 } 2106 } 2107 2108 private void onOpenEntry(Uri logUri, long channelId, long watchStarttime, 2109 SessionState sessionState) { 2110 if (!isChannelSearchable(channelId)) { 2111 // Do not log anything about non-searchable channels. 2112 synchronized (mLock) { 2113 sessionState.mLogUri = null; 2114 } 2115 mContentResolver.delete(logUri, null, null); 2116 return; 2117 } 2118 2119 String[] projection = { 2120 TvContract.Programs.COLUMN_TITLE, 2121 TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, 2122 TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, 2123 TvContract.Programs.COLUMN_SHORT_DESCRIPTION 2124 }; 2125 String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND " 2126 + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND " 2127 + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS + ">?"; 2128 String[] selectionArgs = { 2129 String.valueOf(channelId), 2130 String.valueOf(watchStarttime), 2131 String.valueOf(watchStarttime) 2132 }; 2133 String sortOrder = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC"; 2134 Cursor cursor = null; 2135 try { 2136 cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection, 2137 selection, selectionArgs, sortOrder); 2138 if (cursor != null && cursor.moveToNext()) { 2139 ContentValues values = new ContentValues(); 2140 values.put(TvContract.WatchedPrograms.COLUMN_TITLE, cursor.getString(0)); 2141 values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, 2142 cursor.getLong(1)); 2143 long endTime = cursor.getLong(2); 2144 values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); 2145 values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, cursor.getString(3)); 2146 mContentResolver.update(logUri, values, null, null); 2147 2148 // Schedule an update when the current program ends. 2149 SomeArgs args = SomeArgs.obtain(); 2150 args.arg1 = logUri; 2151 args.arg2 = channelId; 2152 args.arg3 = endTime; 2153 Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args); 2154 sendMessageDelayed(msg, endTime - System.currentTimeMillis()); 2155 } 2156 } finally { 2157 if (cursor != null) { 2158 cursor.close(); 2159 } 2160 } 2161 } 2162 2163 private void onUpdateEntry(Uri uri, long channelId, long time, SessionState sessionState) { 2164 String[] projection = { 2165 TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 2166 TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 2167 TvContract.WatchedPrograms.COLUMN_TITLE, 2168 TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, 2169 TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, 2170 TvContract.WatchedPrograms.COLUMN_DESCRIPTION 2171 }; 2172 Cursor cursor = null; 2173 try { 2174 cursor = mContentResolver.query(uri, projection, null, null, null); 2175 if (cursor != null && cursor.moveToNext()) { 2176 long watchStartTime = cursor.getLong(0); 2177 long watchEndTime = cursor.getLong(1); 2178 String title = cursor.getString(2); 2179 long startTime = cursor.getLong(3); 2180 long endTime = cursor.getLong(4); 2181 String description = cursor.getString(5); 2182 2183 // Do nothing if the current log entry is already closed. 2184 if (watchEndTime > 0) { 2185 return; 2186 } 2187 2188 // The current program has just ended. Create a (complete) log entry off the 2189 // current entry. 2190 ContentValues values = new ContentValues(); 2191 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 2192 watchStartTime); 2193 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, time); 2194 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 2195 values.put(TvContract.WatchedPrograms.COLUMN_TITLE, title); 2196 values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime); 2197 values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); 2198 values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, description); 2199 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 2200 } 2201 } finally { 2202 if (cursor != null) { 2203 cursor.close(); 2204 } 2205 } 2206 // Re-open the current log entry with the next program information. 2207 onOpenEntry(uri, channelId, time, sessionState); 2208 } 2209 2210 private void onCloseEntry(Uri uri, long watchEndTime) { 2211 ContentValues values = new ContentValues(); 2212 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, watchEndTime); 2213 mContentResolver.update(uri, values, null, null); 2214 } 2215 2216 private boolean isChannelSearchable(long channelId) { 2217 String[] projection = { TvContract.Channels.COLUMN_SEARCHABLE }; 2218 String selection = TvContract.Channels._ID + "=?"; 2219 String[] selectionArgs = { String.valueOf(channelId) }; 2220 Cursor cursor = null; 2221 try { 2222 cursor = mContentResolver.query(TvContract.Channels.CONTENT_URI, projection, 2223 selection, selectionArgs, null); 2224 if (cursor != null && cursor.moveToNext()) { 2225 return cursor.getLong(0) == 1 ? true : false; 2226 } 2227 } finally { 2228 if (cursor != null) { 2229 cursor.close(); 2230 } 2231 } 2232 // Unless explicitly specified non-searchable, by default the channel is searchable. 2233 return true; 2234 } 2235 } 2236 2237 final class HardwareListener implements TvInputHardwareManager.Listener { 2238 @Override 2239 public void onStateChanged(String inputId, int state) { 2240 synchronized (mLock) { 2241 setStateLocked(inputId, state, mCurrentUserId); 2242 } 2243 } 2244 2245 @Override 2246 public void onHardwareDeviceAdded(TvInputHardwareInfo info) { 2247 synchronized (mLock) { 2248 UserState userState = getUserStateLocked(mCurrentUserId); 2249 // Broadcast the event to all hardware inputs. 2250 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2251 if (!serviceState.mIsHardware || serviceState.mService == null) continue; 2252 try { 2253 serviceState.mService.notifyHardwareAdded(info); 2254 } catch (RemoteException e) { 2255 Slog.e(TAG, "error in notifyHardwareAdded", e); 2256 } 2257 } 2258 } 2259 } 2260 2261 @Override 2262 public void onHardwareDeviceRemoved(TvInputHardwareInfo info) { 2263 synchronized (mLock) { 2264 UserState userState = getUserStateLocked(mCurrentUserId); 2265 // Broadcast the event to all hardware inputs. 2266 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2267 if (!serviceState.mIsHardware || serviceState.mService == null) continue; 2268 try { 2269 serviceState.mService.notifyHardwareRemoved(info); 2270 } catch (RemoteException e) { 2271 Slog.e(TAG, "error in notifyHardwareRemoved", e); 2272 } 2273 } 2274 } 2275 } 2276 2277 @Override 2278 public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { 2279 synchronized (mLock) { 2280 UserState userState = getUserStateLocked(mCurrentUserId); 2281 // Broadcast the event to all hardware inputs. 2282 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2283 if (!serviceState.mIsHardware || serviceState.mService == null) continue; 2284 try { 2285 serviceState.mService.notifyHdmiDeviceAdded(deviceInfo); 2286 } catch (RemoteException e) { 2287 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 2288 } 2289 } 2290 } 2291 } 2292 2293 @Override 2294 public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { 2295 synchronized (mLock) { 2296 UserState userState = getUserStateLocked(mCurrentUserId); 2297 // Broadcast the event to all hardware inputs. 2298 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2299 if (!serviceState.mIsHardware || serviceState.mService == null) continue; 2300 try { 2301 serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo); 2302 } catch (RemoteException e) { 2303 Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e); 2304 } 2305 } 2306 } 2307 } 2308 } 2309} 2310