TvInputManagerService.java revision 2263fb0b1a58282fb5a0aecb4c6b4e77296506b7
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.tv; 18 19import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; 20import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY; 21 22import android.annotation.Nullable; 23import android.app.ActivityManager; 24import android.content.BroadcastReceiver; 25import android.content.ComponentName; 26import android.content.ContentProviderOperation; 27import android.content.ContentProviderResult; 28import android.content.ContentResolver; 29import android.content.ContentUris; 30import android.content.ContentValues; 31import android.content.Context; 32import android.content.Intent; 33import android.content.IntentFilter; 34import android.content.OperationApplicationException; 35import android.content.ServiceConnection; 36import android.content.pm.ActivityInfo; 37import android.content.pm.PackageManager; 38import android.content.pm.PackageManager.NameNotFoundException; 39import android.content.pm.ResolveInfo; 40import android.content.pm.ServiceInfo; 41import android.graphics.Rect; 42import android.hardware.hdmi.HdmiControlManager; 43import android.hardware.hdmi.HdmiDeviceInfo; 44import android.media.PlaybackParams; 45import android.media.tv.DvbDeviceInfo; 46import android.media.tv.ITvInputClient; 47import android.media.tv.ITvInputHardware; 48import android.media.tv.ITvInputHardwareCallback; 49import android.media.tv.ITvInputManager; 50import android.media.tv.ITvInputManagerCallback; 51import android.media.tv.ITvInputService; 52import android.media.tv.ITvInputServiceCallback; 53import android.media.tv.ITvInputSession; 54import android.media.tv.ITvInputSessionCallback; 55import android.media.tv.TvContentRating; 56import android.media.tv.TvContentRatingSystemInfo; 57import android.media.tv.TvContract; 58import android.media.tv.TvInputHardwareInfo; 59import android.media.tv.TvInputInfo; 60import android.media.tv.TvInputManager; 61import android.media.tv.TvInputService; 62import android.media.tv.TvStreamConfig; 63import android.media.tv.TvTrackInfo; 64import android.net.Uri; 65import android.os.Binder; 66import android.os.Bundle; 67import android.os.Handler; 68import android.os.IBinder; 69import android.os.Looper; 70import android.os.Message; 71import android.os.ParcelFileDescriptor; 72import android.os.Process; 73import android.os.RemoteException; 74import android.os.UserHandle; 75import android.text.TextUtils; 76import android.util.Slog; 77import android.util.SparseArray; 78import android.view.InputChannel; 79import android.view.Surface; 80 81import com.android.internal.content.PackageMonitor; 82import com.android.internal.os.SomeArgs; 83import com.android.internal.util.IndentingPrintWriter; 84import com.android.server.IoThread; 85import com.android.server.SystemService; 86 87import org.xmlpull.v1.XmlPullParserException; 88 89import java.io.File; 90import java.io.FileDescriptor; 91import java.io.FileNotFoundException; 92import java.io.IOException; 93import java.io.PrintWriter; 94import java.util.ArrayList; 95import java.util.Arrays; 96import java.util.Collections; 97import java.util.HashMap; 98import java.util.HashSet; 99import java.util.Iterator; 100import java.util.List; 101import java.util.Map; 102import java.util.Set; 103import java.util.regex.Matcher; 104import java.util.regex.Pattern; 105 106/** This class provides a system service that manages television inputs. */ 107public final class TvInputManagerService extends SystemService { 108 private static final boolean DEBUG = false; 109 private static final String TAG = "TvInputManagerService"; 110 111 // Pattern for selecting the DVB frontend devices from the list of files in the /dev directory. 112 private static final Pattern sFrontEndDevicePattern = 113 Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$"); 114 115 private final Context mContext; 116 private final TvInputHardwareManager mTvInputHardwareManager; 117 118 // A global lock. 119 private final Object mLock = new Object(); 120 121 // ID of the current user. 122 private int mCurrentUserId = UserHandle.USER_SYSTEM; 123 124 // A map from user id to UserState. 125 private final SparseArray<UserState> mUserStates = new SparseArray<>(); 126 127 private final WatchLogHandler mWatchLogHandler; 128 129 public TvInputManagerService(Context context) { 130 super(context); 131 132 mContext = context; 133 mWatchLogHandler = new WatchLogHandler(mContext.getContentResolver(), 134 IoThread.get().getLooper()); 135 mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener()); 136 137 synchronized (mLock) { 138 getOrCreateUserStateLocked(mCurrentUserId); 139 } 140 } 141 142 @Override 143 public void onStart() { 144 publishBinderService(Context.TV_INPUT_SERVICE, new BinderService()); 145 } 146 147 @Override 148 public void onBootPhase(int phase) { 149 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 150 registerBroadcastReceivers(); 151 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 152 synchronized (mLock) { 153 buildTvInputListLocked(mCurrentUserId, null); 154 buildTvContentRatingSystemListLocked(mCurrentUserId); 155 } 156 } 157 mTvInputHardwareManager.onBootPhase(phase); 158 } 159 160 @Override 161 public void onUnlockUser(int userHandle) { 162 if (DEBUG) Slog.d(TAG, "onUnlockUser(userHandle=" + userHandle + ")"); 163 synchronized (mLock) { 164 if (mCurrentUserId != userHandle) { 165 return; 166 } 167 buildTvInputListLocked(mCurrentUserId, null); 168 buildTvContentRatingSystemListLocked(mCurrentUserId); 169 } 170 } 171 172 private void registerBroadcastReceivers() { 173 PackageMonitor monitor = new PackageMonitor() { 174 private void buildTvInputList(String[] packages) { 175 synchronized (mLock) { 176 if (mCurrentUserId == getChangingUserId()) { 177 buildTvInputListLocked(mCurrentUserId, packages); 178 buildTvContentRatingSystemListLocked(mCurrentUserId); 179 } 180 } 181 } 182 183 @Override 184 public void onPackageUpdateFinished(String packageName, int uid) { 185 if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")"); 186 // This callback is invoked when the TV input is reinstalled. 187 // In this case, isReplacing() always returns true. 188 buildTvInputList(new String[] { packageName }); 189 } 190 191 @Override 192 public void onPackagesAvailable(String[] packages) { 193 if (DEBUG) { 194 Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")"); 195 } 196 // This callback is invoked when the media on which some packages exist become 197 // available. 198 if (isReplacing()) { 199 buildTvInputList(packages); 200 } 201 } 202 203 @Override 204 public void onPackagesUnavailable(String[] packages) { 205 // This callback is invoked when the media on which some packages exist become 206 // unavailable. 207 if (DEBUG) { 208 Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages) 209 + ")"); 210 } 211 if (isReplacing()) { 212 buildTvInputList(packages); 213 } 214 } 215 216 @Override 217 public void onSomePackagesChanged() { 218 // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage 219 // the TV inputs. 220 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()"); 221 if (isReplacing()) { 222 if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing"); 223 // When the package is updated, buildTvInputListLocked is called in other 224 // methods instead. 225 return; 226 } 227 buildTvInputList(null); 228 } 229 230 @Override 231 public boolean onPackageChanged(String packageName, int uid, String[] components) { 232 // The input list needs to be updated in any cases, regardless of whether 233 // it happened to the whole package or a specific component. Returning true so that 234 // the update can be handled in {@link #onSomePackagesChanged}. 235 return true; 236 } 237 238 @Override 239 public void onPackageRemoved(String packageName, int uid) { 240 synchronized (mLock) { 241 UserState userState = getOrCreateUserStateLocked(getChangingUserId()); 242 if (!userState.packageSet.contains(packageName)) { 243 // Not a TV input package. 244 return; 245 } 246 } 247 248 ArrayList<ContentProviderOperation> operations = new ArrayList<>(); 249 250 String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?"; 251 String[] selectionArgs = { packageName }; 252 253 operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI) 254 .withSelection(selection, selectionArgs).build()); 255 operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI) 256 .withSelection(selection, selectionArgs).build()); 257 operations.add(ContentProviderOperation 258 .newDelete(TvContract.WatchedPrograms.CONTENT_URI) 259 .withSelection(selection, selectionArgs).build()); 260 261 ContentProviderResult[] results = null; 262 try { 263 ContentResolver cr = getContentResolverForUser(getChangingUserId()); 264 results = cr.applyBatch(TvContract.AUTHORITY, operations); 265 } catch (RemoteException | OperationApplicationException e) { 266 Slog.e(TAG, "error in applyBatch", e); 267 } 268 269 if (DEBUG) { 270 Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid 271 + ")"); 272 Slog.d(TAG, "results=" + results); 273 } 274 } 275 }; 276 monitor.register(mContext, null, UserHandle.ALL, true); 277 278 IntentFilter intentFilter = new IntentFilter(); 279 intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 280 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 281 mContext.registerReceiverAsUser(new BroadcastReceiver() { 282 @Override 283 public void onReceive(Context context, Intent intent) { 284 String action = intent.getAction(); 285 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 286 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 287 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 288 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 289 } 290 } 291 }, UserHandle.ALL, intentFilter, null, null); 292 } 293 294 private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) { 295 return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, 296 component.getPackageName()) == PackageManager.PERMISSION_GRANTED; 297 } 298 299 private void buildTvInputListLocked(int userId, String[] updatedPackages) { 300 UserState userState = getOrCreateUserStateLocked(userId); 301 userState.packageSet.clear(); 302 303 if (DEBUG) Slog.d(TAG, "buildTvInputList"); 304 PackageManager pm = mContext.getPackageManager(); 305 List<ResolveInfo> services = pm.queryIntentServicesAsUser( 306 new Intent(TvInputService.SERVICE_INTERFACE), 307 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, 308 userId); 309 List<TvInputInfo> inputList = new ArrayList<>(); 310 for (ResolveInfo ri : services) { 311 ServiceInfo si = ri.serviceInfo; 312 if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { 313 Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission " 314 + android.Manifest.permission.BIND_TV_INPUT); 315 continue; 316 } 317 318 ComponentName component = new ComponentName(si.packageName, si.name); 319 if (hasHardwarePermission(pm, component)) { 320 ServiceState serviceState = userState.serviceStateMap.get(component); 321 if (serviceState == null) { 322 // New hardware input found. Create a new ServiceState and connect to the 323 // service to populate the hardware list. 324 serviceState = new ServiceState(component, userId); 325 userState.serviceStateMap.put(component, serviceState); 326 updateServiceConnectionLocked(component, userId); 327 } else { 328 inputList.addAll(serviceState.hardwareInputList); 329 } 330 } else { 331 try { 332 TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build(); 333 inputList.add(info); 334 } catch (XmlPullParserException | IOException e) { 335 Slog.e(TAG, "failed to load TV input " + si.name, e); 336 continue; 337 } 338 } 339 userState.packageSet.add(si.packageName); 340 } 341 342 Map<String, TvInputState> inputMap = new HashMap<>(); 343 for (TvInputInfo info : inputList) { 344 if (DEBUG) { 345 Slog.d(TAG, "add " + info.getId()); 346 } 347 TvInputState inputState = userState.inputMap.get(info.getId()); 348 if (inputState == null) { 349 inputState = new TvInputState(); 350 } 351 inputState.info = info; 352 inputMap.put(info.getId(), inputState); 353 } 354 355 for (String inputId : inputMap.keySet()) { 356 if (!userState.inputMap.containsKey(inputId)) { 357 notifyInputAddedLocked(userState, inputId); 358 } else if (updatedPackages != null) { 359 // Notify the package updates 360 ComponentName component = inputMap.get(inputId).info.getComponent(); 361 for (String updatedPackage : updatedPackages) { 362 if (component.getPackageName().equals(updatedPackage)) { 363 updateServiceConnectionLocked(component, userId); 364 notifyInputUpdatedLocked(userState, inputId); 365 break; 366 } 367 } 368 } 369 } 370 371 for (String inputId : userState.inputMap.keySet()) { 372 if (!inputMap.containsKey(inputId)) { 373 TvInputInfo info = userState.inputMap.get(inputId).info; 374 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 375 if (serviceState != null) { 376 abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId); 377 } 378 notifyInputRemovedLocked(userState, inputId); 379 } 380 } 381 382 userState.inputMap.clear(); 383 userState.inputMap = inputMap; 384 } 385 386 private void buildTvContentRatingSystemListLocked(int userId) { 387 UserState userState = getOrCreateUserStateLocked(userId); 388 userState.contentRatingSystemList.clear(); 389 390 final PackageManager pm = mContext.getPackageManager(); 391 Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS); 392 for (ResolveInfo resolveInfo : 393 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) { 394 ActivityInfo receiver = resolveInfo.activityInfo; 395 Bundle metaData = receiver.metaData; 396 if (metaData == null) { 397 continue; 398 } 399 400 int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS); 401 if (xmlResId == 0) { 402 Slog.w(TAG, "Missing meta-data '" 403 + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver " 404 + receiver.packageName + "/" + receiver.name); 405 continue; 406 } 407 userState.contentRatingSystemList.add( 408 TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId, 409 receiver.applicationInfo)); 410 } 411 } 412 413 private void switchUser(int userId) { 414 synchronized (mLock) { 415 if (mCurrentUserId == userId) { 416 return; 417 } 418 UserState userState = mUserStates.get(mCurrentUserId); 419 List<SessionState> sessionStatesToRelease = new ArrayList<>(); 420 for (SessionState sessionState : userState.sessionStateMap.values()) { 421 if (sessionState.session != null && !sessionState.isRecordingSession) { 422 sessionStatesToRelease.add(sessionState); 423 } 424 } 425 for (SessionState sessionState : sessionStatesToRelease) { 426 try { 427 sessionState.session.release(); 428 } catch (RemoteException e) { 429 Slog.e(TAG, "error in release", e); 430 } 431 clearSessionAndNotifyClientLocked(sessionState); 432 } 433 434 for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator(); 435 it.hasNext(); ) { 436 ComponentName component = it.next(); 437 ServiceState serviceState = userState.serviceStateMap.get(component); 438 if (serviceState != null && serviceState.sessionTokens.isEmpty()) { 439 if (serviceState.callback != null) { 440 try { 441 serviceState.service.unregisterCallback(serviceState.callback); 442 } catch (RemoteException e) { 443 Slog.e(TAG, "error in unregisterCallback", e); 444 } 445 } 446 mContext.unbindService(serviceState.connection); 447 it.remove(); 448 } 449 } 450 451 mCurrentUserId = userId; 452 getOrCreateUserStateLocked(userId); 453 buildTvInputListLocked(userId, null); 454 buildTvContentRatingSystemListLocked(userId); 455 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_SWITCH_CONTENT_RESOLVER, 456 getContentResolverForUser(userId)).sendToTarget(); 457 } 458 } 459 460 private void clearSessionAndNotifyClientLocked(SessionState state) { 461 if (state.client != null) { 462 try { 463 state.client.onSessionReleased(state.seq); 464 } catch(RemoteException e) { 465 Slog.e(TAG, "error in onSessionReleased", e); 466 } 467 } 468 // If there are any other sessions based on this session, they should be released. 469 UserState userState = getOrCreateUserStateLocked(state.userId); 470 for (SessionState sessionState : userState.sessionStateMap.values()) { 471 if (state.sessionToken == sessionState.hardwareSessionToken) { 472 releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID, state.userId); 473 try { 474 sessionState.client.onSessionReleased(sessionState.seq); 475 } catch (RemoteException e) { 476 Slog.e(TAG, "error in onSessionReleased", e); 477 } 478 } 479 } 480 removeSessionStateLocked(state.sessionToken, state.userId); 481 } 482 483 private void removeUser(int userId) { 484 synchronized (mLock) { 485 UserState userState = mUserStates.get(userId); 486 if (userState == null) { 487 return; 488 } 489 // Release all created sessions. 490 for (SessionState state : userState.sessionStateMap.values()) { 491 if (state.session != null) { 492 try { 493 state.session.release(); 494 } catch (RemoteException e) { 495 Slog.e(TAG, "error in release", e); 496 } 497 } 498 } 499 userState.sessionStateMap.clear(); 500 501 // Unregister all callbacks and unbind all services. 502 for (ServiceState serviceState : userState.serviceStateMap.values()) { 503 if (serviceState.service != null) { 504 if (serviceState.callback != null) { 505 try { 506 serviceState.service.unregisterCallback(serviceState.callback); 507 } catch (RemoteException e) { 508 Slog.e(TAG, "error in unregisterCallback", e); 509 } 510 } 511 mContext.unbindService(serviceState.connection); 512 } 513 } 514 userState.serviceStateMap.clear(); 515 516 // Clear everything else. 517 userState.inputMap.clear(); 518 userState.packageSet.clear(); 519 userState.contentRatingSystemList.clear(); 520 userState.clientStateMap.clear(); 521 userState.callbackSet.clear(); 522 userState.mainSessionToken = null; 523 524 mUserStates.remove(userId); 525 } 526 } 527 528 private ContentResolver getContentResolverForUser(int userId) { 529 UserHandle user = new UserHandle(userId); 530 Context context; 531 try { 532 context = mContext.createPackageContextAsUser("android", 0, user); 533 } catch (NameNotFoundException e) { 534 Slog.e(TAG, "failed to create package context as user " + user); 535 context = mContext; 536 } 537 return context.getContentResolver(); 538 } 539 540 private UserState getOrCreateUserStateLocked(int userId) { 541 UserState userState = mUserStates.get(userId); 542 if (userState == null) { 543 userState = new UserState(mContext, userId); 544 mUserStates.put(userId, userState); 545 } 546 return userState; 547 } 548 549 private ServiceState getServiceStateLocked(ComponentName component, int userId) { 550 UserState userState = getOrCreateUserStateLocked(userId); 551 ServiceState serviceState = userState.serviceStateMap.get(component); 552 if (serviceState == null) { 553 throw new IllegalStateException("Service state not found for " + component + " (userId=" 554 + userId + ")"); 555 } 556 return serviceState; 557 } 558 559 private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) { 560 UserState userState = getOrCreateUserStateLocked(userId); 561 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 562 if (sessionState == null) { 563 throw new SessionNotFoundException("Session state not found for token " + sessionToken); 564 } 565 // Only the application that requested this session or the system can access it. 566 if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) { 567 throw new SecurityException("Illegal access to the session with token " + sessionToken 568 + " from uid " + callingUid); 569 } 570 return sessionState; 571 } 572 573 private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) { 574 return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId)); 575 } 576 577 private ITvInputSession getSessionLocked(SessionState sessionState) { 578 ITvInputSession session = sessionState.session; 579 if (session == null) { 580 throw new IllegalStateException("Session not yet created for token " 581 + sessionState.sessionToken); 582 } 583 return session; 584 } 585 586 private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId, 587 String methodName) { 588 return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false, 589 false, methodName, null); 590 } 591 592 private void updateServiceConnectionLocked(ComponentName component, int userId) { 593 UserState userState = getOrCreateUserStateLocked(userId); 594 ServiceState serviceState = userState.serviceStateMap.get(component); 595 if (serviceState == null) { 596 return; 597 } 598 if (serviceState.reconnecting) { 599 if (!serviceState.sessionTokens.isEmpty()) { 600 // wait until all the sessions are removed. 601 return; 602 } 603 serviceState.reconnecting = false; 604 } 605 606 boolean shouldBind; 607 if (userId == mCurrentUserId) { 608 shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware; 609 } else { 610 // For a non-current user, 611 // if sessionTokens is not empty, it contains recording sessions only 612 // because other sessions must have been removed while switching user 613 // and non-recording sessions are not created by createSession(). 614 shouldBind = !serviceState.sessionTokens.isEmpty(); 615 } 616 617 if (serviceState.service == null && shouldBind) { 618 // This means that the service is not yet connected but its state indicates that we 619 // have pending requests. Then, connect the service. 620 if (serviceState.bound) { 621 // We have already bound to the service so we don't try to bind again until after we 622 // unbind later on. 623 return; 624 } 625 if (DEBUG) { 626 Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")"); 627 } 628 629 Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component); 630 serviceState.bound = mContext.bindServiceAsUser( 631 i, serviceState.connection, 632 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, 633 new UserHandle(userId)); 634 } else if (serviceState.service != null && !shouldBind) { 635 // This means that the service is already connected but its state indicates that we have 636 // nothing to do with it. Then, disconnect the service. 637 if (DEBUG) { 638 Slog.d(TAG, "unbindService(service=" + component + ")"); 639 } 640 mContext.unbindService(serviceState.connection); 641 userState.serviceStateMap.remove(component); 642 } 643 } 644 645 private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState, 646 String inputId, int userId) { 647 // Let clients know the create session requests are failed. 648 UserState userState = getOrCreateUserStateLocked(userId); 649 List<SessionState> sessionsToAbort = new ArrayList<>(); 650 for (IBinder sessionToken : serviceState.sessionTokens) { 651 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 652 if (sessionState.session == null && (inputId == null 653 || sessionState.inputId.equals(inputId))) { 654 sessionsToAbort.add(sessionState); 655 } 656 } 657 for (SessionState sessionState : sessionsToAbort) { 658 removeSessionStateLocked(sessionState.sessionToken, sessionState.userId); 659 sendSessionTokenToClientLocked(sessionState.client, 660 sessionState.inputId, null, null, sessionState.seq); 661 } 662 updateServiceConnectionLocked(serviceState.component, userId); 663 } 664 665 private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken, 666 int userId) { 667 UserState userState = getOrCreateUserStateLocked(userId); 668 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 669 if (DEBUG) { 670 Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")"); 671 } 672 InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString()); 673 674 // Set up a callback to send the session token. 675 ITvInputSessionCallback callback = new SessionCallback(sessionState, channels); 676 677 // Create a session. When failed, send a null token immediately. 678 try { 679 if (sessionState.isRecordingSession) { 680 service.createRecordingSession(callback, sessionState.inputId); 681 } else { 682 service.createSession(channels[1], callback, sessionState.inputId); 683 } 684 } catch (RemoteException e) { 685 Slog.e(TAG, "error in createSession", e); 686 removeSessionStateLocked(sessionToken, userId); 687 sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null, 688 null, sessionState.seq); 689 } 690 channels[1].dispose(); 691 } 692 693 private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId, 694 IBinder sessionToken, InputChannel channel, int seq) { 695 try { 696 client.onSessionCreated(inputId, sessionToken, channel, seq); 697 } catch (RemoteException e) { 698 Slog.e(TAG, "error in onSessionCreated", e); 699 } 700 } 701 702 private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { 703 SessionState sessionState = null; 704 try { 705 sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 706 if (sessionState.session != null) { 707 UserState userState = getOrCreateUserStateLocked(userId); 708 if (sessionToken == userState.mainSessionToken) { 709 setMainLocked(sessionToken, false, callingUid, userId); 710 } 711 sessionState.session.release(); 712 } 713 } catch (RemoteException | SessionNotFoundException e) { 714 Slog.e(TAG, "error in releaseSession", e); 715 } finally { 716 if (sessionState != null) { 717 sessionState.session = null; 718 } 719 } 720 removeSessionStateLocked(sessionToken, userId); 721 } 722 723 private void removeSessionStateLocked(IBinder sessionToken, int userId) { 724 UserState userState = getOrCreateUserStateLocked(userId); 725 if (sessionToken == userState.mainSessionToken) { 726 if (DEBUG) { 727 Slog.d(TAG, "mainSessionToken=null"); 728 } 729 userState.mainSessionToken = null; 730 } 731 732 // Remove the session state from the global session state map of the current user. 733 SessionState sessionState = userState.sessionStateMap.remove(sessionToken); 734 735 if (sessionState == null) { 736 return; 737 } 738 739 // Also remove the session token from the session token list of the current client and 740 // service. 741 ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder()); 742 if (clientState != null) { 743 clientState.sessionTokens.remove(sessionToken); 744 if (clientState.isEmpty()) { 745 userState.clientStateMap.remove(sessionState.client.asBinder()); 746 } 747 } 748 749 ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName); 750 if (serviceState != null) { 751 serviceState.sessionTokens.remove(sessionToken); 752 } 753 updateServiceConnectionLocked(sessionState.componentName, userId); 754 755 // Log the end of watch. 756 SomeArgs args = SomeArgs.obtain(); 757 args.arg1 = sessionToken; 758 args.arg2 = System.currentTimeMillis(); 759 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget(); 760 } 761 762 private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) { 763 try { 764 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 765 if (sessionState.hardwareSessionToken != null) { 766 sessionState = getSessionStateLocked(sessionState.hardwareSessionToken, 767 Process.SYSTEM_UID, userId); 768 } 769 ServiceState serviceState = getServiceStateLocked(sessionState.componentName, userId); 770 if (!serviceState.isHardware) { 771 return; 772 } 773 ITvInputSession session = getSessionLocked(sessionState); 774 session.setMain(isMain); 775 } catch (RemoteException | SessionNotFoundException e) { 776 Slog.e(TAG, "error in setMain", e); 777 } 778 } 779 780 private void notifyInputAddedLocked(UserState userState, String inputId) { 781 if (DEBUG) { 782 Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")"); 783 } 784 for (ITvInputManagerCallback callback : userState.callbackSet) { 785 try { 786 callback.onInputAdded(inputId); 787 } catch (RemoteException e) { 788 Slog.e(TAG, "failed to report added input to callback", e); 789 } 790 } 791 } 792 793 private void notifyInputRemovedLocked(UserState userState, String inputId) { 794 if (DEBUG) { 795 Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")"); 796 } 797 for (ITvInputManagerCallback callback : userState.callbackSet) { 798 try { 799 callback.onInputRemoved(inputId); 800 } catch (RemoteException e) { 801 Slog.e(TAG, "failed to report removed input to callback", e); 802 } 803 } 804 } 805 806 private void notifyInputUpdatedLocked(UserState userState, String inputId) { 807 if (DEBUG) { 808 Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")"); 809 } 810 for (ITvInputManagerCallback callback : userState.callbackSet) { 811 try { 812 callback.onInputUpdated(inputId); 813 } catch (RemoteException e) { 814 Slog.e(TAG, "failed to report updated input to callback", e); 815 } 816 } 817 } 818 819 private void notifyInputStateChangedLocked(UserState userState, String inputId, 820 int state, ITvInputManagerCallback targetCallback) { 821 if (DEBUG) { 822 Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId 823 + ", state=" + state + ")"); 824 } 825 if (targetCallback == null) { 826 for (ITvInputManagerCallback callback : userState.callbackSet) { 827 try { 828 callback.onInputStateChanged(inputId, state); 829 } catch (RemoteException e) { 830 Slog.e(TAG, "failed to report state change to callback", e); 831 } 832 } 833 } else { 834 try { 835 targetCallback.onInputStateChanged(inputId, state); 836 } catch (RemoteException e) { 837 Slog.e(TAG, "failed to report state change to callback", e); 838 } 839 } 840 } 841 842 private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) { 843 if (DEBUG) { 844 Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")"); 845 } 846 String inputId = inputInfo.getId(); 847 TvInputState inputState = userState.inputMap.get(inputId); 848 if (inputState == null) { 849 Slog.e(TAG, "failed to set input info - unknown input id " + inputId); 850 return; 851 } 852 inputState.info = inputInfo; 853 854 for (ITvInputManagerCallback callback : userState.callbackSet) { 855 try { 856 callback.onTvInputInfoUpdated(inputInfo); 857 } catch (RemoteException e) { 858 Slog.e(TAG, "failed to report updated input info to callback", e); 859 } 860 } 861 } 862 863 private void setStateLocked(String inputId, int state, int userId) { 864 UserState userState = getOrCreateUserStateLocked(userId); 865 TvInputState inputState = userState.inputMap.get(inputId); 866 ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent()); 867 int oldState = inputState.state; 868 inputState.state = state; 869 if (serviceState != null && serviceState.service == null 870 && (!serviceState.sessionTokens.isEmpty() || serviceState.isHardware)) { 871 // We don't notify state change while reconnecting. It should remain disconnected. 872 return; 873 } 874 if (oldState != state) { 875 notifyInputStateChangedLocked(userState, inputId, state, null); 876 } 877 } 878 879 private final class BinderService extends ITvInputManager.Stub { 880 @Override 881 public List<TvInputInfo> getTvInputList(int userId) { 882 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 883 Binder.getCallingUid(), userId, "getTvInputList"); 884 final long identity = Binder.clearCallingIdentity(); 885 try { 886 synchronized (mLock) { 887 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 888 List<TvInputInfo> inputList = new ArrayList<>(); 889 for (TvInputState state : userState.inputMap.values()) { 890 inputList.add(state.info); 891 } 892 return inputList; 893 } 894 } finally { 895 Binder.restoreCallingIdentity(identity); 896 } 897 } 898 899 @Override 900 public TvInputInfo getTvInputInfo(String inputId, int userId) { 901 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 902 Binder.getCallingUid(), userId, "getTvInputInfo"); 903 final long identity = Binder.clearCallingIdentity(); 904 try { 905 synchronized (mLock) { 906 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 907 TvInputState state = userState.inputMap.get(inputId); 908 return state == null ? null : state.info; 909 } 910 } finally { 911 Binder.restoreCallingIdentity(identity); 912 } 913 } 914 915 public void updateTvInputInfo(TvInputInfo inputInfo, int userId) { 916 String inputInfoPackageName = inputInfo.getServiceInfo().packageName; 917 String callingPackageName = getCallingPackageName(); 918 if (!TextUtils.equals(inputInfoPackageName, callingPackageName)) { 919 throw new IllegalArgumentException("calling package " + callingPackageName 920 + " is not allowed to change TvInputInfo for " + inputInfoPackageName); 921 } 922 923 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 924 Binder.getCallingUid(), userId, "updateTvInputInfo"); 925 final long identity = Binder.clearCallingIdentity(); 926 try { 927 synchronized (mLock) { 928 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 929 updateTvInputInfoLocked(userState, inputInfo); 930 } 931 } finally { 932 Binder.restoreCallingIdentity(identity); 933 } 934 } 935 936 private String getCallingPackageName() { 937 final String[] packages = mContext.getPackageManager().getPackagesForUid( 938 Binder.getCallingUid()); 939 if (packages != null && packages.length > 0) { 940 return packages[0]; 941 } 942 return "unknown"; 943 } 944 945 @Override 946 public int getTvInputState(String inputId, int userId) { 947 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 948 Binder.getCallingUid(), userId, "getTvInputState"); 949 final long identity = Binder.clearCallingIdentity(); 950 try { 951 synchronized (mLock) { 952 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 953 TvInputState state = userState.inputMap.get(inputId); 954 return state == null ? INPUT_STATE_CONNECTED : state.state; 955 } 956 } finally { 957 Binder.restoreCallingIdentity(identity); 958 } 959 } 960 961 @Override 962 public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) { 963 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 964 Binder.getCallingUid(), userId, "getTvContentRatingSystemList"); 965 final long identity = Binder.clearCallingIdentity(); 966 try { 967 synchronized (mLock) { 968 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 969 return userState.contentRatingSystemList; 970 } 971 } finally { 972 Binder.restoreCallingIdentity(identity); 973 } 974 } 975 976 @Override 977 public void registerCallback(final ITvInputManagerCallback callback, int userId) { 978 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 979 Binder.getCallingUid(), userId, "registerCallback"); 980 final long identity = Binder.clearCallingIdentity(); 981 try { 982 synchronized (mLock) { 983 final UserState userState = getOrCreateUserStateLocked(resolvedUserId); 984 userState.callbackSet.add(callback); 985 try { 986 callback.asBinder().linkToDeath(new IBinder.DeathRecipient() { 987 @Override 988 public void binderDied() { 989 synchronized (mLock) { 990 if (userState.callbackSet != null) { 991 userState.callbackSet.remove(callback); 992 } 993 } 994 } 995 }, 0); 996 } catch (RemoteException e) { 997 Slog.e(TAG, "client process has already died", e); 998 } 999 } 1000 } finally { 1001 Binder.restoreCallingIdentity(identity); 1002 } 1003 } 1004 1005 @Override 1006 public void unregisterCallback(ITvInputManagerCallback callback, int userId) { 1007 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1008 Binder.getCallingUid(), userId, "unregisterCallback"); 1009 final long identity = Binder.clearCallingIdentity(); 1010 try { 1011 synchronized (mLock) { 1012 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1013 userState.callbackSet.remove(callback); 1014 } 1015 } finally { 1016 Binder.restoreCallingIdentity(identity); 1017 } 1018 } 1019 1020 @Override 1021 public boolean isParentalControlsEnabled(int userId) { 1022 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1023 Binder.getCallingUid(), userId, "isParentalControlsEnabled"); 1024 final long identity = Binder.clearCallingIdentity(); 1025 try { 1026 synchronized (mLock) { 1027 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1028 return userState.persistentDataStore.isParentalControlsEnabled(); 1029 } 1030 } finally { 1031 Binder.restoreCallingIdentity(identity); 1032 } 1033 } 1034 1035 @Override 1036 public void setParentalControlsEnabled(boolean enabled, int userId) { 1037 ensureParentalControlsPermission(); 1038 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1039 Binder.getCallingUid(), userId, "setParentalControlsEnabled"); 1040 final long identity = Binder.clearCallingIdentity(); 1041 try { 1042 synchronized (mLock) { 1043 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1044 userState.persistentDataStore.setParentalControlsEnabled(enabled); 1045 } 1046 } finally { 1047 Binder.restoreCallingIdentity(identity); 1048 } 1049 } 1050 1051 @Override 1052 public boolean isRatingBlocked(String rating, int userId) { 1053 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1054 Binder.getCallingUid(), userId, "isRatingBlocked"); 1055 final long identity = Binder.clearCallingIdentity(); 1056 try { 1057 synchronized (mLock) { 1058 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1059 return userState.persistentDataStore.isRatingBlocked( 1060 TvContentRating.unflattenFromString(rating)); 1061 } 1062 } finally { 1063 Binder.restoreCallingIdentity(identity); 1064 } 1065 } 1066 1067 @Override 1068 public List<String> getBlockedRatings(int userId) { 1069 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1070 Binder.getCallingUid(), userId, "getBlockedRatings"); 1071 final long identity = Binder.clearCallingIdentity(); 1072 try { 1073 synchronized (mLock) { 1074 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1075 List<String> ratings = new ArrayList<>(); 1076 for (TvContentRating rating 1077 : userState.persistentDataStore.getBlockedRatings()) { 1078 ratings.add(rating.flattenToString()); 1079 } 1080 return ratings; 1081 } 1082 } finally { 1083 Binder.restoreCallingIdentity(identity); 1084 } 1085 } 1086 1087 @Override 1088 public void addBlockedRating(String rating, int userId) { 1089 ensureParentalControlsPermission(); 1090 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1091 Binder.getCallingUid(), userId, "addBlockedRating"); 1092 final long identity = Binder.clearCallingIdentity(); 1093 try { 1094 synchronized (mLock) { 1095 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1096 userState.persistentDataStore.addBlockedRating( 1097 TvContentRating.unflattenFromString(rating)); 1098 } 1099 } finally { 1100 Binder.restoreCallingIdentity(identity); 1101 } 1102 } 1103 1104 @Override 1105 public void removeBlockedRating(String rating, int userId) { 1106 ensureParentalControlsPermission(); 1107 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1108 Binder.getCallingUid(), userId, "removeBlockedRating"); 1109 final long identity = Binder.clearCallingIdentity(); 1110 try { 1111 synchronized (mLock) { 1112 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1113 userState.persistentDataStore.removeBlockedRating( 1114 TvContentRating.unflattenFromString(rating)); 1115 } 1116 } finally { 1117 Binder.restoreCallingIdentity(identity); 1118 } 1119 } 1120 1121 private void ensureParentalControlsPermission() { 1122 if (mContext.checkCallingPermission( 1123 android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) 1124 != PackageManager.PERMISSION_GRANTED) { 1125 throw new SecurityException( 1126 "The caller does not have parental controls permission"); 1127 } 1128 } 1129 1130 @Override 1131 public void createSession(final ITvInputClient client, final String inputId, 1132 boolean isRecordingSession, int seq, int userId) { 1133 final int callingUid = Binder.getCallingUid(); 1134 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1135 userId, "createSession"); 1136 final long identity = Binder.clearCallingIdentity(); 1137 try { 1138 synchronized (mLock) { 1139 if (userId != mCurrentUserId && !isRecordingSession) { 1140 // A non-recording session of a backgroud (non-current) user 1141 // should not be created. 1142 // Let the client get onConnectionFailed callback for this case. 1143 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 1144 return; 1145 } 1146 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1147 TvInputState inputState = userState.inputMap.get(inputId); 1148 if (inputState == null) { 1149 Slog.w(TAG, "Failed to find input state for inputId=" + inputId); 1150 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 1151 return; 1152 } 1153 TvInputInfo info = inputState.info; 1154 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 1155 if (serviceState == null) { 1156 serviceState = new ServiceState(info.getComponent(), resolvedUserId); 1157 userState.serviceStateMap.put(info.getComponent(), serviceState); 1158 } 1159 // Send a null token immediately while reconnecting. 1160 if (serviceState.reconnecting) { 1161 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 1162 return; 1163 } 1164 1165 // Create a new session token and a session state. 1166 IBinder sessionToken = new Binder(); 1167 SessionState sessionState = new SessionState(sessionToken, info.getId(), 1168 info.getComponent(), isRecordingSession, client, seq, callingUid, 1169 resolvedUserId); 1170 1171 // Add them to the global session state map of the current user. 1172 userState.sessionStateMap.put(sessionToken, sessionState); 1173 1174 // Also, add them to the session state map of the current service. 1175 serviceState.sessionTokens.add(sessionToken); 1176 1177 if (serviceState.service != null) { 1178 createSessionInternalLocked(serviceState.service, sessionToken, 1179 resolvedUserId); 1180 } else { 1181 updateServiceConnectionLocked(info.getComponent(), resolvedUserId); 1182 } 1183 } 1184 } finally { 1185 Binder.restoreCallingIdentity(identity); 1186 } 1187 } 1188 1189 @Override 1190 public void releaseSession(IBinder sessionToken, int userId) { 1191 if (DEBUG) { 1192 Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")"); 1193 } 1194 final int callingUid = Binder.getCallingUid(); 1195 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1196 userId, "releaseSession"); 1197 final long identity = Binder.clearCallingIdentity(); 1198 try { 1199 synchronized (mLock) { 1200 releaseSessionLocked(sessionToken, callingUid, resolvedUserId); 1201 } 1202 } finally { 1203 Binder.restoreCallingIdentity(identity); 1204 } 1205 } 1206 1207 @Override 1208 public void setMainSession(IBinder sessionToken, int userId) { 1209 if (DEBUG) { 1210 Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")"); 1211 } 1212 final int callingUid = Binder.getCallingUid(); 1213 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1214 userId, "setMainSession"); 1215 final long identity = Binder.clearCallingIdentity(); 1216 try { 1217 synchronized (mLock) { 1218 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1219 if (userState.mainSessionToken == sessionToken) { 1220 return; 1221 } 1222 if (DEBUG) { 1223 Slog.d(TAG, "mainSessionToken=" + sessionToken); 1224 } 1225 IBinder oldMainSessionToken = userState.mainSessionToken; 1226 userState.mainSessionToken = sessionToken; 1227 1228 // Inform the new main session first. 1229 // See {@link TvInputService.Session#onSetMain}. 1230 if (sessionToken != null) { 1231 setMainLocked(sessionToken, true, callingUid, userId); 1232 } 1233 if (oldMainSessionToken != null) { 1234 setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId); 1235 } 1236 } 1237 } finally { 1238 Binder.restoreCallingIdentity(identity); 1239 } 1240 } 1241 1242 @Override 1243 public void setSurface(IBinder sessionToken, Surface surface, int userId) { 1244 final int callingUid = Binder.getCallingUid(); 1245 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1246 userId, "setSurface"); 1247 final long identity = Binder.clearCallingIdentity(); 1248 try { 1249 synchronized (mLock) { 1250 try { 1251 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1252 resolvedUserId); 1253 if (sessionState.hardwareSessionToken == null) { 1254 getSessionLocked(sessionState).setSurface(surface); 1255 } else { 1256 getSessionLocked(sessionState.hardwareSessionToken, 1257 Process.SYSTEM_UID, resolvedUserId).setSurface(surface); 1258 } 1259 } catch (RemoteException | SessionNotFoundException e) { 1260 Slog.e(TAG, "error in setSurface", e); 1261 } 1262 } 1263 } finally { 1264 if (surface != null) { 1265 // surface is not used in TvInputManagerService. 1266 surface.release(); 1267 } 1268 Binder.restoreCallingIdentity(identity); 1269 } 1270 } 1271 1272 @Override 1273 public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width, 1274 int height, int userId) { 1275 final int callingUid = Binder.getCallingUid(); 1276 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1277 userId, "dispatchSurfaceChanged"); 1278 final long identity = Binder.clearCallingIdentity(); 1279 try { 1280 synchronized (mLock) { 1281 try { 1282 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1283 resolvedUserId); 1284 getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, 1285 height); 1286 if (sessionState.hardwareSessionToken != null) { 1287 getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID, 1288 resolvedUserId).dispatchSurfaceChanged(format, width, height); 1289 } 1290 } catch (RemoteException | SessionNotFoundException e) { 1291 Slog.e(TAG, "error in dispatchSurfaceChanged", e); 1292 } 1293 } 1294 } finally { 1295 Binder.restoreCallingIdentity(identity); 1296 } 1297 } 1298 1299 @Override 1300 public void setVolume(IBinder sessionToken, float volume, int userId) { 1301 final float REMOTE_VOLUME_ON = 1.0f; 1302 final float REMOTE_VOLUME_OFF = 0f; 1303 final int callingUid = Binder.getCallingUid(); 1304 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1305 userId, "setVolume"); 1306 final long identity = Binder.clearCallingIdentity(); 1307 try { 1308 synchronized (mLock) { 1309 try { 1310 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1311 resolvedUserId); 1312 getSessionLocked(sessionState).setVolume(volume); 1313 if (sessionState.hardwareSessionToken != null) { 1314 // Here, we let the hardware session know only whether volume is on or 1315 // off to prevent that the volume is controlled in the both side. 1316 getSessionLocked(sessionState.hardwareSessionToken, 1317 Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f) 1318 ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF); 1319 } 1320 } catch (RemoteException | SessionNotFoundException e) { 1321 Slog.e(TAG, "error in setVolume", e); 1322 } 1323 } 1324 } finally { 1325 Binder.restoreCallingIdentity(identity); 1326 } 1327 } 1328 1329 @Override 1330 public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) { 1331 final int callingUid = Binder.getCallingUid(); 1332 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1333 userId, "tune"); 1334 final long identity = Binder.clearCallingIdentity(); 1335 try { 1336 synchronized (mLock) { 1337 try { 1338 getSessionLocked(sessionToken, callingUid, resolvedUserId).tune( 1339 channelUri, params); 1340 if (TvContract.isChannelUriForPassthroughInput(channelUri)) { 1341 // Do not log the watch history for passthrough inputs. 1342 return; 1343 } 1344 1345 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1346 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 1347 if (sessionState.isRecordingSession) { 1348 return; 1349 } 1350 1351 // Log the start of watch. 1352 SomeArgs args = SomeArgs.obtain(); 1353 args.arg1 = sessionState.componentName.getPackageName(); 1354 args.arg2 = System.currentTimeMillis(); 1355 args.arg3 = ContentUris.parseId(channelUri); 1356 args.arg4 = params; 1357 args.arg5 = sessionToken; 1358 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args) 1359 .sendToTarget(); 1360 } catch (RemoteException | SessionNotFoundException e) { 1361 Slog.e(TAG, "error in tune", e); 1362 } 1363 } 1364 } finally { 1365 Binder.restoreCallingIdentity(identity); 1366 } 1367 } 1368 1369 @Override 1370 public void unblockContent( 1371 IBinder sessionToken, String unblockedRating, int userId) { 1372 ensureParentalControlsPermission(); 1373 final int callingUid = Binder.getCallingUid(); 1374 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1375 userId, "unblockContent"); 1376 final long identity = Binder.clearCallingIdentity(); 1377 try { 1378 synchronized (mLock) { 1379 try { 1380 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1381 .unblockContent(unblockedRating); 1382 } catch (RemoteException | SessionNotFoundException e) { 1383 Slog.e(TAG, "error in unblockContent", e); 1384 } 1385 } 1386 } finally { 1387 Binder.restoreCallingIdentity(identity); 1388 } 1389 } 1390 1391 @Override 1392 public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) { 1393 final int callingUid = Binder.getCallingUid(); 1394 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1395 userId, "setCaptionEnabled"); 1396 final long identity = Binder.clearCallingIdentity(); 1397 try { 1398 synchronized (mLock) { 1399 try { 1400 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1401 .setCaptionEnabled(enabled); 1402 } catch (RemoteException | SessionNotFoundException e) { 1403 Slog.e(TAG, "error in setCaptionEnabled", e); 1404 } 1405 } 1406 } finally { 1407 Binder.restoreCallingIdentity(identity); 1408 } 1409 } 1410 1411 @Override 1412 public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) { 1413 final int callingUid = Binder.getCallingUid(); 1414 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1415 userId, "selectTrack"); 1416 final long identity = Binder.clearCallingIdentity(); 1417 try { 1418 synchronized (mLock) { 1419 try { 1420 getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack( 1421 type, trackId); 1422 } catch (RemoteException | SessionNotFoundException e) { 1423 Slog.e(TAG, "error in selectTrack", e); 1424 } 1425 } 1426 } finally { 1427 Binder.restoreCallingIdentity(identity); 1428 } 1429 } 1430 1431 @Override 1432 public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data, 1433 int userId) { 1434 final int callingUid = Binder.getCallingUid(); 1435 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1436 userId, "sendAppPrivateCommand"); 1437 final long identity = Binder.clearCallingIdentity(); 1438 try { 1439 synchronized (mLock) { 1440 try { 1441 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1442 .appPrivateCommand(command, data); 1443 } catch (RemoteException | SessionNotFoundException e) { 1444 Slog.e(TAG, "error in appPrivateCommand", e); 1445 } 1446 } 1447 } finally { 1448 Binder.restoreCallingIdentity(identity); 1449 } 1450 } 1451 1452 @Override 1453 public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame, 1454 int userId) { 1455 final int callingUid = Binder.getCallingUid(); 1456 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1457 userId, "createOverlayView"); 1458 final long identity = Binder.clearCallingIdentity(); 1459 try { 1460 synchronized (mLock) { 1461 try { 1462 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1463 .createOverlayView(windowToken, frame); 1464 } catch (RemoteException | SessionNotFoundException e) { 1465 Slog.e(TAG, "error in createOverlayView", e); 1466 } 1467 } 1468 } finally { 1469 Binder.restoreCallingIdentity(identity); 1470 } 1471 } 1472 1473 @Override 1474 public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) { 1475 final int callingUid = Binder.getCallingUid(); 1476 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1477 userId, "relayoutOverlayView"); 1478 final long identity = Binder.clearCallingIdentity(); 1479 try { 1480 synchronized (mLock) { 1481 try { 1482 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1483 .relayoutOverlayView(frame); 1484 } catch (RemoteException | SessionNotFoundException e) { 1485 Slog.e(TAG, "error in relayoutOverlayView", e); 1486 } 1487 } 1488 } finally { 1489 Binder.restoreCallingIdentity(identity); 1490 } 1491 } 1492 1493 @Override 1494 public void removeOverlayView(IBinder sessionToken, int userId) { 1495 final int callingUid = Binder.getCallingUid(); 1496 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1497 userId, "removeOverlayView"); 1498 final long identity = Binder.clearCallingIdentity(); 1499 try { 1500 synchronized (mLock) { 1501 try { 1502 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1503 .removeOverlayView(); 1504 } catch (RemoteException | SessionNotFoundException e) { 1505 Slog.e(TAG, "error in removeOverlayView", e); 1506 } 1507 } 1508 } finally { 1509 Binder.restoreCallingIdentity(identity); 1510 } 1511 } 1512 1513 @Override 1514 public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) { 1515 final int callingUid = Binder.getCallingUid(); 1516 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1517 userId, "timeShiftPlay"); 1518 final long identity = Binder.clearCallingIdentity(); 1519 try { 1520 synchronized (mLock) { 1521 try { 1522 getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay( 1523 recordedProgramUri); 1524 } catch (RemoteException | SessionNotFoundException e) { 1525 Slog.e(TAG, "error in timeShiftPlay", e); 1526 } 1527 } 1528 } finally { 1529 Binder.restoreCallingIdentity(identity); 1530 } 1531 } 1532 1533 @Override 1534 public void timeShiftPause(IBinder sessionToken, int userId) { 1535 final int callingUid = Binder.getCallingUid(); 1536 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1537 userId, "timeShiftPause"); 1538 final long identity = Binder.clearCallingIdentity(); 1539 try { 1540 synchronized (mLock) { 1541 try { 1542 getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause(); 1543 } catch (RemoteException | SessionNotFoundException e) { 1544 Slog.e(TAG, "error in timeShiftPause", e); 1545 } 1546 } 1547 } finally { 1548 Binder.restoreCallingIdentity(identity); 1549 } 1550 } 1551 1552 @Override 1553 public void timeShiftResume(IBinder sessionToken, int userId) { 1554 final int callingUid = Binder.getCallingUid(); 1555 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1556 userId, "timeShiftResume"); 1557 final long identity = Binder.clearCallingIdentity(); 1558 try { 1559 synchronized (mLock) { 1560 try { 1561 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1562 .timeShiftResume(); 1563 } catch (RemoteException | SessionNotFoundException e) { 1564 Slog.e(TAG, "error in timeShiftResume", e); 1565 } 1566 } 1567 } finally { 1568 Binder.restoreCallingIdentity(identity); 1569 } 1570 } 1571 1572 @Override 1573 public void timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId) { 1574 final int callingUid = Binder.getCallingUid(); 1575 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1576 userId, "timeShiftSeekTo"); 1577 final long identity = Binder.clearCallingIdentity(); 1578 try { 1579 synchronized (mLock) { 1580 try { 1581 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1582 .timeShiftSeekTo(timeMs); 1583 } catch (RemoteException | SessionNotFoundException e) { 1584 Slog.e(TAG, "error in timeShiftSeekTo", e); 1585 } 1586 } 1587 } finally { 1588 Binder.restoreCallingIdentity(identity); 1589 } 1590 } 1591 1592 @Override 1593 public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params, 1594 int userId) { 1595 final int callingUid = Binder.getCallingUid(); 1596 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1597 userId, "timeShiftSetPlaybackParams"); 1598 final long identity = Binder.clearCallingIdentity(); 1599 try { 1600 synchronized (mLock) { 1601 try { 1602 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1603 .timeShiftSetPlaybackParams(params); 1604 } catch (RemoteException | SessionNotFoundException e) { 1605 Slog.e(TAG, "error in timeShiftSetPlaybackParams", e); 1606 } 1607 } 1608 } finally { 1609 Binder.restoreCallingIdentity(identity); 1610 } 1611 } 1612 1613 @Override 1614 public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable, 1615 int userId) { 1616 final int callingUid = Binder.getCallingUid(); 1617 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1618 userId, "timeShiftEnablePositionTracking"); 1619 final long identity = Binder.clearCallingIdentity(); 1620 try { 1621 synchronized (mLock) { 1622 try { 1623 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1624 .timeShiftEnablePositionTracking(enable); 1625 } catch (RemoteException | SessionNotFoundException e) { 1626 Slog.e(TAG, "error in timeShiftEnablePositionTracking", e); 1627 } 1628 } 1629 } finally { 1630 Binder.restoreCallingIdentity(identity); 1631 } 1632 } 1633 1634 @Override 1635 public void startRecording(IBinder sessionToken, @Nullable Uri programUri, int userId) { 1636 final int callingUid = Binder.getCallingUid(); 1637 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1638 userId, "startRecording"); 1639 final long identity = Binder.clearCallingIdentity(); 1640 try { 1641 synchronized (mLock) { 1642 try { 1643 getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording( 1644 programUri); 1645 } catch (RemoteException | SessionNotFoundException e) { 1646 Slog.e(TAG, "error in startRecording", e); 1647 } 1648 } 1649 } finally { 1650 Binder.restoreCallingIdentity(identity); 1651 } 1652 } 1653 1654 @Override 1655 public void stopRecording(IBinder sessionToken, int userId) { 1656 final int callingUid = Binder.getCallingUid(); 1657 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1658 userId, "stopRecording"); 1659 final long identity = Binder.clearCallingIdentity(); 1660 try { 1661 synchronized (mLock) { 1662 try { 1663 getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording(); 1664 } catch (RemoteException | SessionNotFoundException e) { 1665 Slog.e(TAG, "error in stopRecording", e); 1666 } 1667 } 1668 } finally { 1669 Binder.restoreCallingIdentity(identity); 1670 } 1671 } 1672 1673 @Override 1674 public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { 1675 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1676 != PackageManager.PERMISSION_GRANTED) { 1677 return null; 1678 } 1679 1680 final long identity = Binder.clearCallingIdentity(); 1681 try { 1682 return mTvInputHardwareManager.getHardwareList(); 1683 } finally { 1684 Binder.restoreCallingIdentity(identity); 1685 } 1686 } 1687 1688 @Override 1689 public ITvInputHardware acquireTvInputHardware(int deviceId, 1690 ITvInputHardwareCallback callback, TvInputInfo info, int userId) 1691 throws RemoteException { 1692 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1693 != PackageManager.PERMISSION_GRANTED) { 1694 return null; 1695 } 1696 1697 final long identity = Binder.clearCallingIdentity(); 1698 final int callingUid = Binder.getCallingUid(); 1699 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1700 userId, "acquireTvInputHardware"); 1701 try { 1702 return mTvInputHardwareManager.acquireHardware( 1703 deviceId, callback, info, callingUid, resolvedUserId); 1704 } finally { 1705 Binder.restoreCallingIdentity(identity); 1706 } 1707 } 1708 1709 @Override 1710 public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) 1711 throws RemoteException { 1712 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1713 != PackageManager.PERMISSION_GRANTED) { 1714 return; 1715 } 1716 1717 final long identity = Binder.clearCallingIdentity(); 1718 final int callingUid = Binder.getCallingUid(); 1719 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1720 userId, "releaseTvInputHardware"); 1721 try { 1722 mTvInputHardwareManager.releaseHardware( 1723 deviceId, hardware, callingUid, resolvedUserId); 1724 } finally { 1725 Binder.restoreCallingIdentity(identity); 1726 } 1727 } 1728 1729 @Override 1730 public List<DvbDeviceInfo> getDvbDeviceList() throws RemoteException { 1731 if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE) 1732 != PackageManager.PERMISSION_GRANTED) { 1733 throw new SecurityException("Requires DVB_DEVICE permission"); 1734 } 1735 1736 final long identity = Binder.clearCallingIdentity(); 1737 try { 1738 ArrayList<DvbDeviceInfo> deviceInfos = new ArrayList<>(); 1739 File devDirectory = new File("/dev"); 1740 for (String fileName : devDirectory.list()) { 1741 Matcher matcher = sFrontEndDevicePattern.matcher(fileName); 1742 if (matcher.find()) { 1743 int adapterId = Integer.parseInt(matcher.group(1)); 1744 int deviceId = Integer.parseInt(matcher.group(2)); 1745 deviceInfos.add(new DvbDeviceInfo(adapterId, deviceId)); 1746 } 1747 } 1748 return Collections.unmodifiableList(deviceInfos); 1749 } finally { 1750 Binder.restoreCallingIdentity(identity); 1751 } 1752 } 1753 1754 @Override 1755 public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device) 1756 throws RemoteException { 1757 if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE) 1758 != PackageManager.PERMISSION_GRANTED) { 1759 throw new SecurityException("Requires DVB_DEVICE permission"); 1760 } 1761 1762 final long identity = Binder.clearCallingIdentity(); 1763 try { 1764 String deviceFileName; 1765 switch (device) { 1766 case TvInputManager.DVB_DEVICE_DEMUX: 1767 deviceFileName = String.format("/dev/dvb%d.demux%d", info.getAdapterId(), 1768 info.getDeviceId()); 1769 break; 1770 case TvInputManager.DVB_DEVICE_DVR: 1771 deviceFileName = String.format("/dev/dvb%d.dvr%d", info.getAdapterId(), 1772 info.getDeviceId()); 1773 break; 1774 case TvInputManager.DVB_DEVICE_FRONTEND: 1775 deviceFileName = String.format("/dev/dvb%d.frontend%d", info.getAdapterId(), 1776 info.getDeviceId()); 1777 break; 1778 default: 1779 throw new IllegalArgumentException("Invalid DVB device: " + device); 1780 } 1781 try { 1782 // The DVB frontend device only needs to be opened in read/write mode, which 1783 // allows performing tuning operations. The DVB demux and DVR device are enough 1784 // to be opened in read only mode. 1785 return ParcelFileDescriptor.open(new File(deviceFileName), 1786 TvInputManager.DVB_DEVICE_FRONTEND == device 1787 ? ParcelFileDescriptor.MODE_READ_WRITE 1788 : ParcelFileDescriptor.MODE_READ_ONLY); 1789 } catch (FileNotFoundException e) { 1790 return null; 1791 } 1792 } finally { 1793 Binder.restoreCallingIdentity(identity); 1794 } 1795 } 1796 1797 @Override 1798 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId) 1799 throws RemoteException { 1800 if (mContext.checkCallingPermission( 1801 android.Manifest.permission.CAPTURE_TV_INPUT) 1802 != PackageManager.PERMISSION_GRANTED) { 1803 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 1804 } 1805 1806 final long identity = Binder.clearCallingIdentity(); 1807 final int callingUid = Binder.getCallingUid(); 1808 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1809 userId, "getAvailableTvStreamConfigList"); 1810 try { 1811 return mTvInputHardwareManager.getAvailableTvStreamConfigList( 1812 inputId, callingUid, resolvedUserId); 1813 } finally { 1814 Binder.restoreCallingIdentity(identity); 1815 } 1816 } 1817 1818 @Override 1819 public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config, 1820 int userId) 1821 throws RemoteException { 1822 if (mContext.checkCallingPermission( 1823 android.Manifest.permission.CAPTURE_TV_INPUT) 1824 != PackageManager.PERMISSION_GRANTED) { 1825 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 1826 } 1827 1828 final long identity = Binder.clearCallingIdentity(); 1829 final int callingUid = Binder.getCallingUid(); 1830 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1831 userId, "captureFrame"); 1832 try { 1833 String hardwareInputId = null; 1834 synchronized (mLock) { 1835 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1836 if (userState.inputMap.get(inputId) == null) { 1837 Slog.e(TAG, "input not found for " + inputId); 1838 return false; 1839 } 1840 for (SessionState sessionState : userState.sessionStateMap.values()) { 1841 if (sessionState.inputId.equals(inputId) 1842 && sessionState.hardwareSessionToken != null) { 1843 hardwareInputId = userState.sessionStateMap.get( 1844 sessionState.hardwareSessionToken).inputId; 1845 break; 1846 } 1847 } 1848 } 1849 return mTvInputHardwareManager.captureFrame( 1850 (hardwareInputId != null) ? hardwareInputId : inputId, 1851 surface, config, callingUid, resolvedUserId); 1852 } finally { 1853 Binder.restoreCallingIdentity(identity); 1854 } 1855 } 1856 1857 @Override 1858 public boolean isSingleSessionActive(int userId) throws RemoteException { 1859 final long identity = Binder.clearCallingIdentity(); 1860 final int callingUid = Binder.getCallingUid(); 1861 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1862 userId, "isSingleSessionActive"); 1863 try { 1864 synchronized (mLock) { 1865 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1866 if (userState.sessionStateMap.size() == 1) { 1867 return true; 1868 } else if (userState.sessionStateMap.size() == 2) { 1869 SessionState[] sessionStates = userState.sessionStateMap.values().toArray( 1870 new SessionState[2]); 1871 // Check if there is a wrapper input. 1872 if (sessionStates[0].hardwareSessionToken != null 1873 || sessionStates[1].hardwareSessionToken != null) { 1874 return true; 1875 } 1876 } 1877 return false; 1878 } 1879 } finally { 1880 Binder.restoreCallingIdentity(identity); 1881 } 1882 } 1883 1884 @Override 1885 @SuppressWarnings("resource") 1886 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 1887 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 1888 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1889 != PackageManager.PERMISSION_GRANTED) { 1890 pw.println("Permission Denial: can't dump TvInputManager from pid=" 1891 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 1892 return; 1893 } 1894 1895 synchronized (mLock) { 1896 pw.println("User Ids (Current user: " + mCurrentUserId + "):"); 1897 pw.increaseIndent(); 1898 for (int i = 0; i < mUserStates.size(); i++) { 1899 int userId = mUserStates.keyAt(i); 1900 pw.println(Integer.valueOf(userId)); 1901 } 1902 pw.decreaseIndent(); 1903 1904 for (int i = 0; i < mUserStates.size(); i++) { 1905 int userId = mUserStates.keyAt(i); 1906 UserState userState = getOrCreateUserStateLocked(userId); 1907 pw.println("UserState (" + userId + "):"); 1908 pw.increaseIndent(); 1909 1910 pw.println("inputMap: inputId -> TvInputState"); 1911 pw.increaseIndent(); 1912 for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) { 1913 pw.println(entry.getKey() + ": " + entry.getValue()); 1914 } 1915 pw.decreaseIndent(); 1916 1917 pw.println("packageSet:"); 1918 pw.increaseIndent(); 1919 for (String packageName : userState.packageSet) { 1920 pw.println(packageName); 1921 } 1922 pw.decreaseIndent(); 1923 1924 pw.println("clientStateMap: ITvInputClient -> ClientState"); 1925 pw.increaseIndent(); 1926 for (Map.Entry<IBinder, ClientState> entry : 1927 userState.clientStateMap.entrySet()) { 1928 ClientState client = entry.getValue(); 1929 pw.println(entry.getKey() + ": " + client); 1930 1931 pw.increaseIndent(); 1932 1933 pw.println("sessionTokens:"); 1934 pw.increaseIndent(); 1935 for (IBinder token : client.sessionTokens) { 1936 pw.println("" + token); 1937 } 1938 pw.decreaseIndent(); 1939 1940 pw.println("clientTokens: " + client.clientToken); 1941 pw.println("userId: " + client.userId); 1942 1943 pw.decreaseIndent(); 1944 } 1945 pw.decreaseIndent(); 1946 1947 pw.println("serviceStateMap: ComponentName -> ServiceState"); 1948 pw.increaseIndent(); 1949 for (Map.Entry<ComponentName, ServiceState> entry : 1950 userState.serviceStateMap.entrySet()) { 1951 ServiceState service = entry.getValue(); 1952 pw.println(entry.getKey() + ": " + service); 1953 1954 pw.increaseIndent(); 1955 1956 pw.println("sessionTokens:"); 1957 pw.increaseIndent(); 1958 for (IBinder token : service.sessionTokens) { 1959 pw.println("" + token); 1960 } 1961 pw.decreaseIndent(); 1962 1963 pw.println("service: " + service.service); 1964 pw.println("callback: " + service.callback); 1965 pw.println("bound: " + service.bound); 1966 pw.println("reconnecting: " + service.reconnecting); 1967 1968 pw.decreaseIndent(); 1969 } 1970 pw.decreaseIndent(); 1971 1972 pw.println("sessionStateMap: ITvInputSession -> SessionState"); 1973 pw.increaseIndent(); 1974 for (Map.Entry<IBinder, SessionState> entry : 1975 userState.sessionStateMap.entrySet()) { 1976 SessionState session = entry.getValue(); 1977 pw.println(entry.getKey() + ": " + session); 1978 1979 pw.increaseIndent(); 1980 pw.println("inputId: " + session.inputId); 1981 pw.println("client: " + session.client); 1982 pw.println("seq: " + session.seq); 1983 pw.println("callingUid: " + session.callingUid); 1984 pw.println("userId: " + session.userId); 1985 pw.println("sessionToken: " + session.sessionToken); 1986 pw.println("session: " + session.session); 1987 pw.println("logUri: " + session.logUri); 1988 pw.println("hardwareSessionToken: " + session.hardwareSessionToken); 1989 pw.decreaseIndent(); 1990 } 1991 pw.decreaseIndent(); 1992 1993 pw.println("callbackSet:"); 1994 pw.increaseIndent(); 1995 for (ITvInputManagerCallback callback : userState.callbackSet) { 1996 pw.println(callback.toString()); 1997 } 1998 pw.decreaseIndent(); 1999 2000 pw.println("mainSessionToken: " + userState.mainSessionToken); 2001 pw.decreaseIndent(); 2002 } 2003 } 2004 mTvInputHardwareManager.dump(fd, writer, args); 2005 } 2006 } 2007 2008 private static final class UserState { 2009 // A mapping from the TV input id to its TvInputState. 2010 private Map<String, TvInputState> inputMap = new HashMap<>(); 2011 2012 // A set of all TV input packages. 2013 private final Set<String> packageSet = new HashSet<>(); 2014 2015 // A list of all TV content rating systems defined. 2016 private final List<TvContentRatingSystemInfo> 2017 contentRatingSystemList = new ArrayList<>(); 2018 2019 // A mapping from the token of a client to its state. 2020 private final Map<IBinder, ClientState> clientStateMap = new HashMap<>(); 2021 2022 // A mapping from the name of a TV input service to its state. 2023 private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>(); 2024 2025 // A mapping from the token of a TV input session to its state. 2026 private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>(); 2027 2028 // A set of callbacks. 2029 private final Set<ITvInputManagerCallback> callbackSet = new HashSet<>(); 2030 2031 // The token of a "main" TV input session. 2032 private IBinder mainSessionToken = null; 2033 2034 // Persistent data store for all internal settings maintained by the TV input manager 2035 // service. 2036 private final PersistentDataStore persistentDataStore; 2037 2038 private UserState(Context context, int userId) { 2039 persistentDataStore = new PersistentDataStore(context, userId); 2040 } 2041 } 2042 2043 private final class ClientState implements IBinder.DeathRecipient { 2044 private final List<IBinder> sessionTokens = new ArrayList<>(); 2045 2046 private IBinder clientToken; 2047 private final int userId; 2048 2049 ClientState(IBinder clientToken, int userId) { 2050 this.clientToken = clientToken; 2051 this.userId = userId; 2052 } 2053 2054 public boolean isEmpty() { 2055 return sessionTokens.isEmpty(); 2056 } 2057 2058 @Override 2059 public void binderDied() { 2060 synchronized (mLock) { 2061 UserState userState = getOrCreateUserStateLocked(userId); 2062 // DO NOT remove the client state of clientStateMap in this method. It will be 2063 // removed in releaseSessionLocked(). 2064 ClientState clientState = userState.clientStateMap.get(clientToken); 2065 if (clientState != null) { 2066 while (clientState.sessionTokens.size() > 0) { 2067 releaseSessionLocked( 2068 clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId); 2069 } 2070 } 2071 clientToken = null; 2072 } 2073 } 2074 } 2075 2076 private final class ServiceState { 2077 private final List<IBinder> sessionTokens = new ArrayList<>(); 2078 private final ServiceConnection connection; 2079 private final ComponentName component; 2080 private final boolean isHardware; 2081 private final List<TvInputInfo> hardwareInputList = new ArrayList<>(); 2082 2083 private ITvInputService service; 2084 private ServiceCallback callback; 2085 private boolean bound; 2086 private boolean reconnecting; 2087 2088 private ServiceState(ComponentName component, int userId) { 2089 this.component = component; 2090 this.connection = new InputServiceConnection(component, userId); 2091 this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component); 2092 } 2093 } 2094 2095 private static final class TvInputState { 2096 // A TvInputInfo object which represents the TV input. 2097 private TvInputInfo info; 2098 2099 // The state of TV input. Connected by default. 2100 private int state = INPUT_STATE_CONNECTED; 2101 2102 @Override 2103 public String toString() { 2104 return "info: " + info + "; state: " + state; 2105 } 2106 } 2107 2108 private final class SessionState implements IBinder.DeathRecipient { 2109 private final String inputId; 2110 private final ComponentName componentName; 2111 private final boolean isRecordingSession; 2112 private final ITvInputClient client; 2113 private final int seq; 2114 private final int callingUid; 2115 private final int userId; 2116 private final IBinder sessionToken; 2117 private ITvInputSession session; 2118 private Uri logUri; 2119 // Not null if this session represents an external device connected to a hardware TV input. 2120 private IBinder hardwareSessionToken; 2121 2122 private SessionState(IBinder sessionToken, String inputId, ComponentName componentName, 2123 boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, 2124 int userId) { 2125 this.sessionToken = sessionToken; 2126 this.inputId = inputId; 2127 this.componentName = componentName; 2128 this.isRecordingSession = isRecordingSession; 2129 this.client = client; 2130 this.seq = seq; 2131 this.callingUid = callingUid; 2132 this.userId = userId; 2133 } 2134 2135 @Override 2136 public void binderDied() { 2137 synchronized (mLock) { 2138 session = null; 2139 clearSessionAndNotifyClientLocked(this); 2140 } 2141 } 2142 } 2143 2144 private final class InputServiceConnection implements ServiceConnection { 2145 private final ComponentName mComponent; 2146 private final int mUserId; 2147 2148 private InputServiceConnection(ComponentName component, int userId) { 2149 mComponent = component; 2150 mUserId = userId; 2151 } 2152 2153 @Override 2154 public void onServiceConnected(ComponentName component, IBinder service) { 2155 if (DEBUG) { 2156 Slog.d(TAG, "onServiceConnected(component=" + component + ")"); 2157 } 2158 synchronized (mLock) { 2159 UserState userState = mUserStates.get(mUserId); 2160 if (userState == null) { 2161 // The user was removed while connecting. 2162 mContext.unbindService(this); 2163 return; 2164 } 2165 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 2166 serviceState.service = ITvInputService.Stub.asInterface(service); 2167 2168 // Register a callback, if we need to. 2169 if (serviceState.isHardware && serviceState.callback == null) { 2170 serviceState.callback = new ServiceCallback(mComponent, mUserId); 2171 try { 2172 serviceState.service.registerCallback(serviceState.callback); 2173 } catch (RemoteException e) { 2174 Slog.e(TAG, "error in registerCallback", e); 2175 } 2176 } 2177 2178 // And create sessions, if any. 2179 for (IBinder sessionToken : serviceState.sessionTokens) { 2180 createSessionInternalLocked(serviceState.service, sessionToken, mUserId); 2181 } 2182 2183 for (TvInputState inputState : userState.inputMap.values()) { 2184 if (inputState.info.getComponent().equals(component) 2185 && inputState.state != INPUT_STATE_CONNECTED) { 2186 notifyInputStateChangedLocked(userState, inputState.info.getId(), 2187 inputState.state, null); 2188 } 2189 } 2190 2191 if (serviceState.isHardware) { 2192 serviceState.hardwareInputList.clear(); 2193 for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) { 2194 try { 2195 serviceState.service.notifyHardwareAdded(hardware); 2196 } catch (RemoteException e) { 2197 Slog.e(TAG, "error in notifyHardwareAdded", e); 2198 } 2199 } 2200 for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) { 2201 try { 2202 serviceState.service.notifyHdmiDeviceAdded(device); 2203 } catch (RemoteException e) { 2204 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 2205 } 2206 } 2207 } 2208 } 2209 } 2210 2211 @Override 2212 public void onServiceDisconnected(ComponentName component) { 2213 if (DEBUG) { 2214 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")"); 2215 } 2216 if (!mComponent.equals(component)) { 2217 throw new IllegalArgumentException("Mismatched ComponentName: " 2218 + mComponent + " (expected), " + component + " (actual)."); 2219 } 2220 synchronized (mLock) { 2221 UserState userState = getOrCreateUserStateLocked(mUserId); 2222 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 2223 if (serviceState != null) { 2224 serviceState.reconnecting = true; 2225 serviceState.bound = false; 2226 serviceState.service = null; 2227 serviceState.callback = null; 2228 2229 abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId); 2230 } 2231 } 2232 } 2233 } 2234 2235 private final class ServiceCallback extends ITvInputServiceCallback.Stub { 2236 private final ComponentName mComponent; 2237 private final int mUserId; 2238 2239 ServiceCallback(ComponentName component, int userId) { 2240 mComponent = component; 2241 mUserId = userId; 2242 } 2243 2244 private void ensureHardwarePermission() { 2245 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 2246 != PackageManager.PERMISSION_GRANTED) { 2247 throw new SecurityException("The caller does not have hardware permission"); 2248 } 2249 } 2250 2251 private void ensureValidInput(TvInputInfo inputInfo) { 2252 if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) { 2253 throw new IllegalArgumentException("Invalid TvInputInfo"); 2254 } 2255 } 2256 2257 private void addHardwareInputLocked(TvInputInfo inputInfo) { 2258 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 2259 serviceState.hardwareInputList.add(inputInfo); 2260 buildTvInputListLocked(mUserId, null); 2261 } 2262 2263 public void addHardwareInput(int deviceId, TvInputInfo inputInfo) { 2264 ensureHardwarePermission(); 2265 ensureValidInput(inputInfo); 2266 synchronized (mLock) { 2267 mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo); 2268 addHardwareInputLocked(inputInfo); 2269 } 2270 } 2271 2272 public void addHdmiInput(int id, TvInputInfo inputInfo) { 2273 ensureHardwarePermission(); 2274 ensureValidInput(inputInfo); 2275 synchronized (mLock) { 2276 mTvInputHardwareManager.addHdmiInput(id, inputInfo); 2277 addHardwareInputLocked(inputInfo); 2278 } 2279 } 2280 2281 public void removeHardwareInput(String inputId) { 2282 ensureHardwarePermission(); 2283 synchronized (mLock) { 2284 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 2285 boolean removed = false; 2286 for (Iterator<TvInputInfo> it = serviceState.hardwareInputList.iterator(); 2287 it.hasNext(); ) { 2288 if (it.next().getId().equals(inputId)) { 2289 it.remove(); 2290 removed = true; 2291 break; 2292 } 2293 } 2294 if (removed) { 2295 buildTvInputListLocked(mUserId, null); 2296 mTvInputHardwareManager.removeHardwareInput(inputId); 2297 } else { 2298 Slog.e(TAG, "failed to remove input " + inputId); 2299 } 2300 } 2301 } 2302 } 2303 2304 private final class SessionCallback extends ITvInputSessionCallback.Stub { 2305 private final SessionState mSessionState; 2306 private final InputChannel[] mChannels; 2307 2308 SessionCallback(SessionState sessionState, InputChannel[] channels) { 2309 mSessionState = sessionState; 2310 mChannels = channels; 2311 } 2312 2313 @Override 2314 public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) { 2315 if (DEBUG) { 2316 Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")"); 2317 } 2318 synchronized (mLock) { 2319 mSessionState.session = session; 2320 mSessionState.hardwareSessionToken = hardwareSessionToken; 2321 if (session != null && addSessionTokenToClientStateLocked(session)) { 2322 sendSessionTokenToClientLocked(mSessionState.client, 2323 mSessionState.inputId, mSessionState.sessionToken, mChannels[0], 2324 mSessionState.seq); 2325 } else { 2326 removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId); 2327 sendSessionTokenToClientLocked(mSessionState.client, 2328 mSessionState.inputId, null, null, mSessionState.seq); 2329 } 2330 mChannels[0].dispose(); 2331 } 2332 } 2333 2334 private boolean addSessionTokenToClientStateLocked(ITvInputSession session) { 2335 try { 2336 session.asBinder().linkToDeath(mSessionState, 0); 2337 } catch (RemoteException e) { 2338 Slog.e(TAG, "session process has already died", e); 2339 return false; 2340 } 2341 2342 IBinder clientToken = mSessionState.client.asBinder(); 2343 UserState userState = getOrCreateUserStateLocked(mSessionState.userId); 2344 ClientState clientState = userState.clientStateMap.get(clientToken); 2345 if (clientState == null) { 2346 clientState = new ClientState(clientToken, mSessionState.userId); 2347 try { 2348 clientToken.linkToDeath(clientState, 0); 2349 } catch (RemoteException e) { 2350 Slog.e(TAG, "client process has already died", e); 2351 return false; 2352 } 2353 userState.clientStateMap.put(clientToken, clientState); 2354 } 2355 clientState.sessionTokens.add(mSessionState.sessionToken); 2356 return true; 2357 } 2358 2359 @Override 2360 public void onChannelRetuned(Uri channelUri) { 2361 synchronized (mLock) { 2362 if (DEBUG) { 2363 Slog.d(TAG, "onChannelRetuned(" + channelUri + ")"); 2364 } 2365 if (mSessionState.session == null || mSessionState.client == null) { 2366 return; 2367 } 2368 try { 2369 // TODO: Consider adding this channel change in the watch log. When we do 2370 // that, how we can protect the watch log from malicious tv inputs should 2371 // be addressed. e.g. add a field which represents where the channel change 2372 // originated from. 2373 mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq); 2374 } catch (RemoteException e) { 2375 Slog.e(TAG, "error in onChannelRetuned", e); 2376 } 2377 } 2378 } 2379 2380 @Override 2381 public void onTracksChanged(List<TvTrackInfo> tracks) { 2382 synchronized (mLock) { 2383 if (DEBUG) { 2384 Slog.d(TAG, "onTracksChanged(" + tracks + ")"); 2385 } 2386 if (mSessionState.session == null || mSessionState.client == null) { 2387 return; 2388 } 2389 try { 2390 mSessionState.client.onTracksChanged(tracks, mSessionState.seq); 2391 } catch (RemoteException e) { 2392 Slog.e(TAG, "error in onTracksChanged", e); 2393 } 2394 } 2395 } 2396 2397 @Override 2398 public void onTrackSelected(int type, String trackId) { 2399 synchronized (mLock) { 2400 if (DEBUG) { 2401 Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")"); 2402 } 2403 if (mSessionState.session == null || mSessionState.client == null) { 2404 return; 2405 } 2406 try { 2407 mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq); 2408 } catch (RemoteException e) { 2409 Slog.e(TAG, "error in onTrackSelected", e); 2410 } 2411 } 2412 } 2413 2414 @Override 2415 public void onVideoAvailable() { 2416 synchronized (mLock) { 2417 if (DEBUG) { 2418 Slog.d(TAG, "onVideoAvailable()"); 2419 } 2420 if (mSessionState.session == null || mSessionState.client == null) { 2421 return; 2422 } 2423 try { 2424 mSessionState.client.onVideoAvailable(mSessionState.seq); 2425 } catch (RemoteException e) { 2426 Slog.e(TAG, "error in onVideoAvailable", e); 2427 } 2428 } 2429 } 2430 2431 @Override 2432 public void onVideoUnavailable(int reason) { 2433 synchronized (mLock) { 2434 if (DEBUG) { 2435 Slog.d(TAG, "onVideoUnavailable(" + reason + ")"); 2436 } 2437 if (mSessionState.session == null || mSessionState.client == null) { 2438 return; 2439 } 2440 try { 2441 mSessionState.client.onVideoUnavailable(reason, mSessionState.seq); 2442 } catch (RemoteException e) { 2443 Slog.e(TAG, "error in onVideoUnavailable", e); 2444 } 2445 } 2446 } 2447 2448 @Override 2449 public void onContentAllowed() { 2450 synchronized (mLock) { 2451 if (DEBUG) { 2452 Slog.d(TAG, "onContentAllowed()"); 2453 } 2454 if (mSessionState.session == null || mSessionState.client == null) { 2455 return; 2456 } 2457 try { 2458 mSessionState.client.onContentAllowed(mSessionState.seq); 2459 } catch (RemoteException e) { 2460 Slog.e(TAG, "error in onContentAllowed", e); 2461 } 2462 } 2463 } 2464 2465 @Override 2466 public void onContentBlocked(String rating) { 2467 synchronized (mLock) { 2468 if (DEBUG) { 2469 Slog.d(TAG, "onContentBlocked()"); 2470 } 2471 if (mSessionState.session == null || mSessionState.client == null) { 2472 return; 2473 } 2474 try { 2475 mSessionState.client.onContentBlocked(rating, mSessionState.seq); 2476 } catch (RemoteException e) { 2477 Slog.e(TAG, "error in onContentBlocked", e); 2478 } 2479 } 2480 } 2481 2482 @Override 2483 public void onLayoutSurface(int left, int top, int right, int bottom) { 2484 synchronized (mLock) { 2485 if (DEBUG) { 2486 Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top 2487 + ", right=" + right + ", bottom=" + bottom + ",)"); 2488 } 2489 if (mSessionState.session == null || mSessionState.client == null) { 2490 return; 2491 } 2492 try { 2493 mSessionState.client.onLayoutSurface(left, top, right, bottom, 2494 mSessionState.seq); 2495 } catch (RemoteException e) { 2496 Slog.e(TAG, "error in onLayoutSurface", e); 2497 } 2498 } 2499 } 2500 2501 @Override 2502 public void onSessionEvent(String eventType, Bundle eventArgs) { 2503 synchronized (mLock) { 2504 if (DEBUG) { 2505 Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs 2506 + ")"); 2507 } 2508 if (mSessionState.session == null || mSessionState.client == null) { 2509 return; 2510 } 2511 try { 2512 mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq); 2513 } catch (RemoteException e) { 2514 Slog.e(TAG, "error in onSessionEvent", e); 2515 } 2516 } 2517 } 2518 2519 @Override 2520 public void onTimeShiftStatusChanged(int status) { 2521 synchronized (mLock) { 2522 if (DEBUG) { 2523 Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")"); 2524 } 2525 if (mSessionState.session == null || mSessionState.client == null) { 2526 return; 2527 } 2528 try { 2529 mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq); 2530 } catch (RemoteException e) { 2531 Slog.e(TAG, "error in onTimeShiftStatusChanged", e); 2532 } 2533 } 2534 } 2535 2536 @Override 2537 public void onTimeShiftStartPositionChanged(long timeMs) { 2538 synchronized (mLock) { 2539 if (DEBUG) { 2540 Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")"); 2541 } 2542 if (mSessionState.session == null || mSessionState.client == null) { 2543 return; 2544 } 2545 try { 2546 mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq); 2547 } catch (RemoteException e) { 2548 Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e); 2549 } 2550 } 2551 } 2552 2553 @Override 2554 public void onTimeShiftCurrentPositionChanged(long timeMs) { 2555 synchronized (mLock) { 2556 if (DEBUG) { 2557 Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")"); 2558 } 2559 if (mSessionState.session == null || mSessionState.client == null) { 2560 return; 2561 } 2562 try { 2563 mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs, 2564 mSessionState.seq); 2565 } catch (RemoteException e) { 2566 Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e); 2567 } 2568 } 2569 } 2570 2571 // For the recording session only 2572 @Override 2573 public void onTuned(Uri channelUri) { 2574 synchronized (mLock) { 2575 if (DEBUG) { 2576 Slog.d(TAG, "onTuned()"); 2577 } 2578 if (mSessionState.session == null || mSessionState.client == null) { 2579 return; 2580 } 2581 try { 2582 mSessionState.client.onTuned(mSessionState.seq, channelUri); 2583 } catch (RemoteException e) { 2584 Slog.e(TAG, "error in onTuned", e); 2585 } 2586 } 2587 } 2588 2589 // For the recording session only 2590 @Override 2591 public void onRecordingStopped(Uri recordedProgramUri) { 2592 synchronized (mLock) { 2593 if (DEBUG) { 2594 Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri 2595 + ")"); 2596 } 2597 if (mSessionState.session == null || mSessionState.client == null) { 2598 return; 2599 } 2600 try { 2601 mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq); 2602 } catch (RemoteException e) { 2603 Slog.e(TAG, "error in onRecordingStopped", e); 2604 } 2605 } 2606 } 2607 2608 // For the recording session only 2609 @Override 2610 public void onError(int error) { 2611 synchronized (mLock) { 2612 if (DEBUG) { 2613 Slog.d(TAG, "onError(error=" + error + ")"); 2614 } 2615 if (mSessionState.session == null || mSessionState.client == null) { 2616 return; 2617 } 2618 try { 2619 mSessionState.client.onError(error, mSessionState.seq); 2620 } catch (RemoteException e) { 2621 Slog.e(TAG, "error in onError", e); 2622 } 2623 } 2624 } 2625 } 2626 2627 private static final class WatchLogHandler extends Handler { 2628 // There are only two kinds of watch events that can happen on the system: 2629 // 1. The current TV input session is tuned to a new channel. 2630 // 2. The session is released for some reason. 2631 // The former indicates the end of the previous log entry, if any, followed by the start of 2632 // a new entry. The latter indicates the end of the most recent entry for the given session. 2633 // Here the system supplies the database the smallest set of information only that is 2634 // sufficient to consolidate the log entries while minimizing database operations in the 2635 // system service. 2636 static final int MSG_LOG_WATCH_START = 1; 2637 static final int MSG_LOG_WATCH_END = 2; 2638 static final int MSG_SWITCH_CONTENT_RESOLVER = 3; 2639 2640 private ContentResolver mContentResolver; 2641 2642 WatchLogHandler(ContentResolver contentResolver, Looper looper) { 2643 super(looper); 2644 mContentResolver = contentResolver; 2645 } 2646 2647 @Override 2648 public void handleMessage(Message msg) { 2649 switch (msg.what) { 2650 case MSG_LOG_WATCH_START: { 2651 SomeArgs args = (SomeArgs) msg.obj; 2652 String packageName = (String) args.arg1; 2653 long watchStartTime = (long) args.arg2; 2654 long channelId = (long) args.arg3; 2655 Bundle tuneParams = (Bundle) args.arg4; 2656 IBinder sessionToken = (IBinder) args.arg5; 2657 2658 ContentValues values = new ContentValues(); 2659 values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName); 2660 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 2661 watchStartTime); 2662 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 2663 if (tuneParams != null) { 2664 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS, 2665 encodeTuneParams(tuneParams)); 2666 } 2667 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN, 2668 sessionToken.toString()); 2669 2670 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 2671 args.recycle(); 2672 break; 2673 } 2674 case MSG_LOG_WATCH_END: { 2675 SomeArgs args = (SomeArgs) msg.obj; 2676 IBinder sessionToken = (IBinder) args.arg1; 2677 long watchEndTime = (long) args.arg2; 2678 2679 ContentValues values = new ContentValues(); 2680 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 2681 watchEndTime); 2682 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN, 2683 sessionToken.toString()); 2684 2685 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 2686 args.recycle(); 2687 break; 2688 } 2689 case MSG_SWITCH_CONTENT_RESOLVER: { 2690 mContentResolver = (ContentResolver) msg.obj; 2691 break; 2692 } 2693 default: { 2694 Slog.w(TAG, "unhandled message code: " + msg.what); 2695 break; 2696 } 2697 } 2698 } 2699 2700 private String encodeTuneParams(Bundle tuneParams) { 2701 StringBuilder builder = new StringBuilder(); 2702 Set<String> keySet = tuneParams.keySet(); 2703 Iterator<String> it = keySet.iterator(); 2704 while (it.hasNext()) { 2705 String key = it.next(); 2706 Object value = tuneParams.get(key); 2707 if (value == null) { 2708 continue; 2709 } 2710 builder.append(replaceEscapeCharacters(key)); 2711 builder.append("="); 2712 builder.append(replaceEscapeCharacters(value.toString())); 2713 if (it.hasNext()) { 2714 builder.append(", "); 2715 } 2716 } 2717 return builder.toString(); 2718 } 2719 2720 private String replaceEscapeCharacters(String src) { 2721 final char ESCAPE_CHARACTER = '%'; 2722 final String ENCODING_TARGET_CHARACTERS = "%=,"; 2723 StringBuilder builder = new StringBuilder(); 2724 for (char ch : src.toCharArray()) { 2725 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) { 2726 builder.append(ESCAPE_CHARACTER); 2727 } 2728 builder.append(ch); 2729 } 2730 return builder.toString(); 2731 } 2732 } 2733 2734 private final class HardwareListener implements TvInputHardwareManager.Listener { 2735 @Override 2736 public void onStateChanged(String inputId, int state) { 2737 synchronized (mLock) { 2738 setStateLocked(inputId, state, mCurrentUserId); 2739 } 2740 } 2741 2742 @Override 2743 public void onHardwareDeviceAdded(TvInputHardwareInfo info) { 2744 synchronized (mLock) { 2745 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 2746 // Broadcast the event to all hardware inputs. 2747 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2748 if (!serviceState.isHardware || serviceState.service == null) continue; 2749 try { 2750 serviceState.service.notifyHardwareAdded(info); 2751 } catch (RemoteException e) { 2752 Slog.e(TAG, "error in notifyHardwareAdded", e); 2753 } 2754 } 2755 } 2756 } 2757 2758 @Override 2759 public void onHardwareDeviceRemoved(TvInputHardwareInfo info) { 2760 synchronized (mLock) { 2761 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 2762 // Broadcast the event to all hardware inputs. 2763 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2764 if (!serviceState.isHardware || serviceState.service == null) continue; 2765 try { 2766 serviceState.service.notifyHardwareRemoved(info); 2767 } catch (RemoteException e) { 2768 Slog.e(TAG, "error in notifyHardwareRemoved", e); 2769 } 2770 } 2771 } 2772 } 2773 2774 @Override 2775 public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { 2776 synchronized (mLock) { 2777 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 2778 // Broadcast the event to all hardware inputs. 2779 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2780 if (!serviceState.isHardware || serviceState.service == null) continue; 2781 try { 2782 serviceState.service.notifyHdmiDeviceAdded(deviceInfo); 2783 } catch (RemoteException e) { 2784 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 2785 } 2786 } 2787 } 2788 } 2789 2790 @Override 2791 public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { 2792 synchronized (mLock) { 2793 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 2794 // Broadcast the event to all hardware inputs. 2795 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2796 if (!serviceState.isHardware || serviceState.service == null) continue; 2797 try { 2798 serviceState.service.notifyHdmiDeviceRemoved(deviceInfo); 2799 } catch (RemoteException e) { 2800 Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e); 2801 } 2802 } 2803 } 2804 } 2805 2806 @Override 2807 public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) { 2808 synchronized (mLock) { 2809 Integer state; 2810 switch (deviceInfo.getDevicePowerStatus()) { 2811 case HdmiControlManager.POWER_STATUS_ON: 2812 state = INPUT_STATE_CONNECTED; 2813 break; 2814 case HdmiControlManager.POWER_STATUS_STANDBY: 2815 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON: 2816 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY: 2817 state = INPUT_STATE_CONNECTED_STANDBY; 2818 break; 2819 case HdmiControlManager.POWER_STATUS_UNKNOWN: 2820 default: 2821 state = null; 2822 break; 2823 } 2824 if (state != null) { 2825 setStateLocked(inputId, state, mCurrentUserId); 2826 } 2827 } 2828 } 2829 } 2830 2831 private static class SessionNotFoundException extends IllegalArgumentException { 2832 public SessionNotFoundException(String name) { 2833 super(name); 2834 } 2835 } 2836} 2837