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 17 package com.android.server.tv; 18 19 import static android.media.AudioManager.DEVICE_NONE; 20 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; 21 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.UserIdInt; 26 import android.app.ActivityManager; 27 import android.content.AttributionSource; 28 import android.content.BroadcastReceiver; 29 import android.content.ComponentName; 30 import android.content.ContentResolver; 31 import android.content.ContentUris; 32 import android.content.ContentValues; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.IntentFilter; 36 import android.content.ServiceConnection; 37 import android.content.pm.ActivityInfo; 38 import android.content.pm.ApplicationInfo; 39 import android.content.pm.PackageManager; 40 import android.content.pm.PackageManager.NameNotFoundException; 41 import android.content.pm.ResolveInfo; 42 import android.content.pm.ServiceInfo; 43 import android.content.pm.UserInfo; 44 import android.graphics.Rect; 45 import android.hardware.hdmi.HdmiControlManager; 46 import android.hardware.hdmi.HdmiDeviceInfo; 47 import android.media.AudioPresentation; 48 import android.media.PlaybackParams; 49 import android.media.tv.AdBuffer; 50 import android.media.tv.AdRequest; 51 import android.media.tv.AdResponse; 52 import android.media.tv.AitInfo; 53 import android.media.tv.BroadcastInfoRequest; 54 import android.media.tv.BroadcastInfoResponse; 55 import android.media.tv.DvbDeviceInfo; 56 import android.media.tv.ITvInputClient; 57 import android.media.tv.ITvInputHardware; 58 import android.media.tv.ITvInputHardwareCallback; 59 import android.media.tv.ITvInputManager; 60 import android.media.tv.ITvInputManagerCallback; 61 import android.media.tv.ITvInputService; 62 import android.media.tv.ITvInputServiceCallback; 63 import android.media.tv.ITvInputSession; 64 import android.media.tv.ITvInputSessionCallback; 65 import android.media.tv.TunedInfo; 66 import android.media.tv.TvContentRating; 67 import android.media.tv.TvContentRatingSystemInfo; 68 import android.media.tv.TvContract; 69 import android.media.tv.TvInputHardwareInfo; 70 import android.media.tv.TvInputInfo; 71 import android.media.tv.TvInputManager; 72 import android.media.tv.TvInputService; 73 import android.media.tv.TvStreamConfig; 74 import android.media.tv.TvTrackInfo; 75 import android.media.tv.tunerresourcemanager.TunerResourceManager; 76 import android.net.Uri; 77 import android.os.Binder; 78 import android.os.Bundle; 79 import android.os.Handler; 80 import android.os.IBinder; 81 import android.os.Looper; 82 import android.os.Message; 83 import android.os.ParcelFileDescriptor; 84 import android.os.Process; 85 import android.os.RemoteCallbackList; 86 import android.os.RemoteException; 87 import android.os.UserHandle; 88 import android.os.UserManager; 89 import android.text.TextUtils; 90 import android.util.ArrayMap; 91 import android.util.Pair; 92 import android.util.Slog; 93 import android.util.SparseArray; 94 import android.view.InputChannel; 95 import android.view.Surface; 96 97 import com.android.internal.annotations.GuardedBy; 98 import com.android.internal.annotations.VisibleForTesting; 99 import com.android.internal.content.PackageMonitor; 100 import com.android.internal.os.SomeArgs; 101 import com.android.internal.util.CollectionUtils; 102 import com.android.internal.util.DumpUtils; 103 import com.android.internal.util.FrameworkStatsLog; 104 import com.android.internal.util.IndentingPrintWriter; 105 import com.android.server.IoThread; 106 import com.android.server.SystemService; 107 108 import dalvik.annotation.optimization.NeverCompile; 109 110 import java.io.File; 111 import java.io.FileDescriptor; 112 import java.io.FileNotFoundException; 113 import java.io.PrintWriter; 114 import java.util.ArrayList; 115 import java.util.Arrays; 116 import java.util.Collections; 117 import java.util.Comparator; 118 import java.util.HashMap; 119 import java.util.HashSet; 120 import java.util.Iterator; 121 import java.util.List; 122 import java.util.Map; 123 import java.util.Objects; 124 import java.util.Set; 125 import java.util.UUID; 126 import java.util.regex.Matcher; 127 import java.util.regex.Pattern; 128 129 /** This class provides a system service that manages television inputs. */ 130 public final class TvInputManagerService extends SystemService { 131 private static final boolean DEBUG = false; 132 private static final String TAG = "TvInputManagerService"; 133 private static final String DVB_DIRECTORY = "/dev/dvb"; 134 private static final int APP_TAG_SELF = TunedInfo.APP_TAG_SELF; 135 private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS = 136 "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"; 137 138 // There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d, 139 // another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the 140 // DVB frontend devices from the list of files in the /dev and /dev/dvb/adapter%d directory. 141 private static final Pattern sFrontEndDevicePattern = 142 Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$"); 143 private static final Pattern sAdapterDirPattern = 144 Pattern.compile("^adapter([0-9]+)$"); 145 private static final Pattern sFrontEndInAdapterDirPattern = 146 Pattern.compile("^frontend([0-9]+)$"); 147 148 private final Context mContext; 149 private final TvInputHardwareManager mTvInputHardwareManager; 150 private final UserManager mUserManager; 151 152 // A global lock. 153 private final Object mLock = new Object(); 154 155 // ID of the current user. 156 @GuardedBy("mLock") 157 private int mCurrentUserId = UserHandle.USER_SYSTEM; 158 // IDs of the running profiles. Their parent user ID should be mCurrentUserId. 159 @GuardedBy("mLock") 160 private final Set<Integer> mRunningProfiles = new HashSet<>(); 161 162 // A map from user id to UserState. 163 @GuardedBy("mLock") 164 private final SparseArray<UserState> mUserStates = new SparseArray<>(); 165 166 // A map from session id to session state saved in userstate 167 @GuardedBy("mLock") 168 private final Map<String, SessionState> mSessionIdToSessionStateMap = new HashMap<>(); 169 170 private final WatchLogHandler mWatchLogHandler; 171 172 private final ActivityManager mActivityManager; 173 TvInputManagerService(Context context)174 public TvInputManagerService(Context context) { 175 super(context); 176 177 mContext = context; 178 mWatchLogHandler = new WatchLogHandler(mContext.getContentResolver(), 179 IoThread.get().getLooper()); 180 mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener()); 181 182 mActivityManager = 183 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); 184 mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); 185 186 synchronized (mLock) { 187 getOrCreateUserStateLocked(mCurrentUserId); 188 } 189 } 190 191 @Override onStart()192 public void onStart() { 193 publishBinderService(Context.TV_INPUT_SERVICE, new BinderService()); 194 } 195 196 @Override onBootPhase(int phase)197 public void onBootPhase(int phase) { 198 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 199 registerBroadcastReceivers(); 200 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 201 synchronized (mLock) { 202 buildTvInputListLocked(mCurrentUserId, null); 203 buildTvContentRatingSystemListLocked(mCurrentUserId); 204 } 205 } 206 mTvInputHardwareManager.onBootPhase(phase); 207 } 208 209 @Override onUserUnlocking(@onNull TargetUser user)210 public void onUserUnlocking(@NonNull TargetUser user) { 211 if (DEBUG) Slog.d(TAG, "onUnlockUser(user=" + user + ")"); 212 synchronized (mLock) { 213 if (mCurrentUserId != user.getUserIdentifier()) { 214 return; 215 } 216 buildTvInputListLocked(mCurrentUserId, null); 217 buildTvContentRatingSystemListLocked(mCurrentUserId); 218 } 219 } 220 registerBroadcastReceivers()221 private void registerBroadcastReceivers() { 222 PackageMonitor monitor = new PackageMonitor() { 223 private void buildTvInputList(String[] packages) { 224 int userId = getChangingUserId(); 225 synchronized (mLock) { 226 if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) { 227 buildTvInputListLocked(userId, packages); 228 buildTvContentRatingSystemListLocked(userId); 229 } 230 } 231 } 232 233 @Override 234 public void onPackageUpdateFinished(String packageName, int uid) { 235 if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")"); 236 // This callback is invoked when the TV input is reinstalled. 237 // In this case, isReplacing() always returns true. 238 buildTvInputList(new String[] { packageName }); 239 } 240 241 @Override 242 public void onPackagesAvailable(String[] packages) { 243 if (DEBUG) { 244 Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")"); 245 } 246 // This callback is invoked when the media on which some packages exist become 247 // available. 248 if (isReplacing()) { 249 buildTvInputList(packages); 250 } 251 } 252 253 @Override 254 public void onPackagesUnavailable(String[] packages) { 255 // This callback is invoked when the media on which some packages exist become 256 // unavailable. 257 if (DEBUG) { 258 Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages) 259 + ")"); 260 } 261 if (isReplacing()) { 262 buildTvInputList(packages); 263 } 264 } 265 266 @Override 267 public void onSomePackagesChanged() { 268 // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage 269 // the TV inputs. 270 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()"); 271 if (isReplacing()) { 272 if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing"); 273 // When the package is updated, buildTvInputListLocked is called in other 274 // methods instead. 275 return; 276 } 277 buildTvInputList(null); 278 } 279 280 @Override 281 public boolean onPackageChanged(String packageName, int uid, String[] components) { 282 // The input list needs to be updated in any cases, regardless of whether 283 // it happened to the whole package or a specific component. Returning true so that 284 // the update can be handled in {@link #onSomePackagesChanged}. 285 return true; 286 } 287 }; 288 monitor.register(mContext, null, UserHandle.ALL, true); 289 290 IntentFilter intentFilter = new IntentFilter(); 291 intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 292 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 293 intentFilter.addAction(Intent.ACTION_USER_STARTED); 294 intentFilter.addAction(Intent.ACTION_USER_STOPPED); 295 mContext.registerReceiverAsUser(new BroadcastReceiver() { 296 @Override 297 public void onReceive(Context context, Intent intent) { 298 String action = intent.getAction(); 299 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 300 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 301 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 302 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 303 } else if (Intent.ACTION_USER_STARTED.equals(action)) { 304 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 305 startUser(userId); 306 } else if (Intent.ACTION_USER_STOPPED.equals(action)) { 307 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 308 stopUser(userId); 309 } 310 } 311 }, UserHandle.ALL, intentFilter, null, null); 312 } 313 hasHardwarePermission(PackageManager pm, ComponentName component)314 private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) { 315 return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, 316 component.getPackageName()) == PackageManager.PERMISSION_GRANTED; 317 } 318 @GuardedBy("mLock") buildTvInputListLocked(int userId, String[] updatedPackages)319 private void buildTvInputListLocked(int userId, String[] updatedPackages) { 320 UserState userState = getOrCreateUserStateLocked(userId); 321 userState.packageSet.clear(); 322 323 if (DEBUG) Slog.d(TAG, "buildTvInputList"); 324 PackageManager pm = mContext.getPackageManager(); 325 List<ResolveInfo> services = pm.queryIntentServicesAsUser( 326 new Intent(TvInputService.SERVICE_INTERFACE), 327 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, 328 userId); 329 List<TvInputInfo> inputList = new ArrayList<>(); 330 List<ComponentName> hardwareComponents = new ArrayList<>(); 331 for (ResolveInfo ri : services) { 332 ServiceInfo si = ri.serviceInfo; 333 if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { 334 Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission " 335 + android.Manifest.permission.BIND_TV_INPUT); 336 continue; 337 } 338 339 ComponentName component = new ComponentName(si.packageName, si.name); 340 if (hasHardwarePermission(pm, component)) { 341 hardwareComponents.add(component); 342 ServiceState serviceState = userState.serviceStateMap.get(component); 343 if (serviceState == null) { 344 // New hardware input found. Create a new ServiceState and connect to the 345 // service to populate the hardware list. 346 serviceState = new ServiceState(component, userId); 347 userState.serviceStateMap.put(component, serviceState); 348 updateServiceConnectionLocked(component, userId); 349 } else { 350 inputList.addAll(serviceState.hardwareInputMap.values()); 351 } 352 } else { 353 try { 354 TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build(); 355 inputList.add(info); 356 } catch (Exception e) { 357 Slog.e(TAG, "failed to load TV input " + si.name, e); 358 continue; 359 } 360 } 361 userState.packageSet.add(si.packageName); 362 } 363 364 // sort the input list by input id so that TvInputState.inputNumber is stable. 365 Collections.sort(inputList, Comparator.comparing(TvInputInfo::getId)); 366 Map<String, TvInputState> inputMap = new HashMap<>(); 367 ArrayMap<String, Integer> tisInputCount = new ArrayMap<>(inputMap.size()); 368 for (TvInputInfo info : inputList) { 369 String inputId = info.getId(); 370 if (DEBUG) { 371 Slog.d(TAG, "add " + inputId); 372 } 373 // Running count of input for each input service 374 Integer count = tisInputCount.get(inputId); 375 count = count == null ? Integer.valueOf(1) : count + 1; 376 tisInputCount.put(inputId, count); 377 TvInputState inputState = userState.inputMap.get(inputId); 378 if (inputState == null) { 379 inputState = new TvInputState(); 380 } 381 inputState.info = info; 382 inputState.uid = getInputUid(info); 383 inputMap.put(inputId, inputState); 384 inputState.inputNumber = count; 385 } 386 387 for (String inputId : inputMap.keySet()) { 388 if (!userState.inputMap.containsKey(inputId)) { 389 notifyInputAddedLocked(userState, inputId); 390 } else if (updatedPackages != null) { 391 // Notify the package updates 392 ComponentName component = inputMap.get(inputId).info.getComponent(); 393 for (String updatedPackage : updatedPackages) { 394 if (component.getPackageName().equals(updatedPackage)) { 395 updateServiceConnectionLocked(component, userId); 396 notifyInputUpdatedLocked(userState, inputId); 397 break; 398 } 399 } 400 } 401 } 402 403 for (String inputId : userState.inputMap.keySet()) { 404 if (!inputMap.containsKey(inputId)) { 405 TvInputInfo info = userState.inputMap.get(inputId).info; 406 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 407 if (serviceState != null) { 408 abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId); 409 } 410 notifyInputRemovedLocked(userState, inputId); 411 } 412 } 413 414 // Clean up ServiceState corresponding to the removed hardware inputs 415 Iterator<ServiceState> it = userState.serviceStateMap.values().iterator(); 416 while (it.hasNext()) { 417 ServiceState serviceState = it.next(); 418 if (serviceState.isHardware && !hardwareComponents.contains(serviceState.component)) { 419 it.remove(); 420 } 421 } 422 423 userState.inputMap.clear(); 424 userState.inputMap = inputMap; 425 } 426 getInputUid(TvInputInfo info)427 private int getInputUid(TvInputInfo info) { 428 try { 429 return getContext().getPackageManager().getApplicationInfo( 430 info.getServiceInfo().packageName, 0).uid; 431 } catch (NameNotFoundException e) { 432 Slog.w(TAG, "Unable to get UID for " + info, e); 433 return Process.INVALID_UID; 434 } 435 } 436 437 @GuardedBy("mLock") buildTvContentRatingSystemListLocked(int userId)438 private void buildTvContentRatingSystemListLocked(int userId) { 439 UserState userState = getOrCreateUserStateLocked(userId); 440 userState.contentRatingSystemList.clear(); 441 442 final PackageManager pm = mContext.getPackageManager(); 443 Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS); 444 for (ResolveInfo resolveInfo : 445 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) { 446 ActivityInfo receiver = resolveInfo.activityInfo; 447 Bundle metaData = receiver.metaData; 448 if (metaData == null) { 449 continue; 450 } 451 452 int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS); 453 if (xmlResId == 0) { 454 Slog.w(TAG, "Missing meta-data '" 455 + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver " 456 + receiver.packageName + "/" + receiver.name); 457 continue; 458 } 459 userState.contentRatingSystemList.add( 460 TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId, 461 receiver.applicationInfo)); 462 } 463 } 464 startUser(int userId)465 private void startUser(int userId) { 466 synchronized (mLock) { 467 if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) { 468 // user already started 469 return; 470 } 471 UserInfo userInfo = mUserManager.getUserInfo(userId); 472 UserInfo parentInfo = mUserManager.getProfileParent(userId); 473 if (userInfo.isProfile() 474 && parentInfo != null 475 && parentInfo.id == mCurrentUserId) { 476 // only the children of the current user can be started in background 477 startProfileLocked(userId); 478 } 479 } 480 } 481 stopUser(int userId)482 private void stopUser(int userId) { 483 synchronized (mLock) { 484 if (userId == mCurrentUserId) { 485 switchUser(ActivityManager.getCurrentUser()); 486 return; 487 } 488 489 releaseSessionOfUserLocked(userId); 490 unbindServiceOfUserLocked(userId); 491 mRunningProfiles.remove(userId); 492 } 493 } 494 startProfileLocked(int userId)495 private void startProfileLocked(int userId) { 496 mRunningProfiles.add(userId); 497 buildTvInputListLocked(userId, null); 498 buildTvContentRatingSystemListLocked(userId); 499 } 500 switchUser(int userId)501 private void switchUser(int userId) { 502 synchronized (mLock) { 503 if (mCurrentUserId == userId) { 504 return; 505 } 506 UserInfo userInfo = mUserManager.getUserInfo(userId); 507 if (userInfo.isProfile()) { 508 Slog.w(TAG, "cannot switch to a profile!"); 509 return; 510 } 511 512 for (int runningId : mRunningProfiles) { 513 releaseSessionOfUserLocked(runningId); 514 unbindServiceOfUserLocked(runningId); 515 } 516 mRunningProfiles.clear(); 517 releaseSessionOfUserLocked(mCurrentUserId); 518 unbindServiceOfUserLocked(mCurrentUserId); 519 520 mCurrentUserId = userId; 521 buildTvInputListLocked(userId, null); 522 buildTvContentRatingSystemListLocked(userId); 523 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_SWITCH_CONTENT_RESOLVER, 524 getContentResolverForUser(userId)).sendToTarget(); 525 } 526 } 527 528 @GuardedBy("mLock") releaseSessionOfUserLocked(int userId)529 private void releaseSessionOfUserLocked(int userId) { 530 UserState userState = getUserStateLocked(userId); 531 if (userState == null) { 532 return; 533 } 534 List<SessionState> sessionStatesToRelease = new ArrayList<>(); 535 for (SessionState sessionState : userState.sessionStateMap.values()) { 536 if (sessionState.session != null && !sessionState.isRecordingSession) { 537 sessionStatesToRelease.add(sessionState); 538 } 539 } 540 boolean notifyInfoUpdated = false; 541 for (SessionState sessionState : sessionStatesToRelease) { 542 try { 543 sessionState.session.release(); 544 sessionState.currentChannel = null; 545 if (sessionState.isCurrent) { 546 sessionState.isCurrent = false; 547 notifyInfoUpdated = true; 548 } 549 } catch (RemoteException e) { 550 Slog.e(TAG, "error in release", e); 551 } finally { 552 if (notifyInfoUpdated) { 553 notifyCurrentChannelInfosUpdatedLocked(userState); 554 } 555 } 556 clearSessionAndNotifyClientLocked(sessionState); 557 } 558 } 559 560 @GuardedBy("mLock") unbindServiceOfUserLocked(int userId)561 private void unbindServiceOfUserLocked(int userId) { 562 UserState userState = getUserStateLocked(userId); 563 if (userState == null) { 564 return; 565 } 566 for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator(); 567 it.hasNext(); ) { 568 ComponentName component = it.next(); 569 ServiceState serviceState = userState.serviceStateMap.get(component); 570 if (serviceState != null && serviceState.sessionTokens.isEmpty()) { 571 if (serviceState.callback != null) { 572 try { 573 serviceState.service.unregisterCallback(serviceState.callback); 574 } catch (RemoteException e) { 575 Slog.e(TAG, "error in unregisterCallback", e); 576 } 577 } 578 mContext.unbindService(serviceState.connection); 579 it.remove(); 580 } 581 } 582 } 583 584 @GuardedBy("mLock") clearSessionAndNotifyClientLocked(SessionState state)585 private void clearSessionAndNotifyClientLocked(SessionState state) { 586 if (state.client != null) { 587 try { 588 state.client.onSessionReleased(state.seq); 589 } catch(RemoteException e) { 590 Slog.e(TAG, "error in onSessionReleased", e); 591 } 592 } 593 // If there are any other sessions based on this session, they should be released. 594 UserState userState = getOrCreateUserStateLocked(state.userId); 595 for (SessionState sessionState : userState.sessionStateMap.values()) { 596 if (state.sessionToken == sessionState.hardwareSessionToken) { 597 releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID, state.userId); 598 try { 599 sessionState.client.onSessionReleased(sessionState.seq); 600 } catch (RemoteException e) { 601 Slog.e(TAG, "error in onSessionReleased", e); 602 } 603 } 604 } 605 removeSessionStateLocked(state.sessionToken, state.userId); 606 } 607 removeUser(int userId)608 private void removeUser(int userId) { 609 synchronized (mLock) { 610 UserState userState = getUserStateLocked(userId); 611 if (userState == null) { 612 return; 613 } 614 // Release all created sessions. 615 boolean notifyInfoUpdated = false; 616 for (SessionState state : userState.sessionStateMap.values()) { 617 if (state.session != null) { 618 try { 619 state.session.release(); 620 state.currentChannel = null; 621 if (state.isCurrent) { 622 state.isCurrent = false; 623 notifyInfoUpdated = true; 624 } 625 } catch (RemoteException e) { 626 Slog.e(TAG, "error in release", e); 627 } finally { 628 if (notifyInfoUpdated) { 629 notifyCurrentChannelInfosUpdatedLocked(userState); 630 } 631 } 632 } 633 } 634 userState.sessionStateMap.clear(); 635 636 // Unregister all callbacks and unbind all services. 637 for (ServiceState serviceState : userState.serviceStateMap.values()) { 638 if (serviceState.service != null) { 639 if (serviceState.callback != null) { 640 try { 641 serviceState.service.unregisterCallback(serviceState.callback); 642 } catch (RemoteException e) { 643 Slog.e(TAG, "error in unregisterCallback", e); 644 } 645 } 646 mContext.unbindService(serviceState.connection); 647 } 648 } 649 userState.serviceStateMap.clear(); 650 651 // Clear everything else. 652 userState.inputMap.clear(); 653 userState.packageSet.clear(); 654 userState.contentRatingSystemList.clear(); 655 userState.clientStateMap.clear(); 656 userState.mCallbacks.kill(); 657 userState.mainSessionToken = null; 658 659 mRunningProfiles.remove(userId); 660 mUserStates.remove(userId); 661 662 if (userId == mCurrentUserId) { 663 switchUser(UserHandle.USER_SYSTEM); 664 } 665 } 666 } 667 getContentResolverForUser(int userId)668 private ContentResolver getContentResolverForUser(int userId) { 669 UserHandle user = new UserHandle(userId); 670 Context context; 671 try { 672 context = mContext.createPackageContextAsUser("android", 0, user); 673 } catch (NameNotFoundException e) { 674 Slog.e(TAG, "failed to create package context as user " + user); 675 context = mContext; 676 } 677 return context.getContentResolver(); 678 } 679 680 @GuardedBy("mLock") getOrCreateUserStateLocked(int userId)681 private UserState getOrCreateUserStateLocked(int userId) { 682 UserState userState = getUserStateLocked(userId); 683 if (userState == null) { 684 userState = new UserState(mContext, userId); 685 mUserStates.put(userId, userState); 686 } 687 return userState; 688 } 689 690 @GuardedBy("mLock") getServiceStateLocked(ComponentName component, int userId)691 private ServiceState getServiceStateLocked(ComponentName component, int userId) { 692 UserState userState = getOrCreateUserStateLocked(userId); 693 ServiceState serviceState = userState.serviceStateMap.get(component); 694 if (serviceState == null) { 695 throw new IllegalStateException("Service state not found for " + component + " (userId=" 696 + userId + ")"); 697 } 698 return serviceState; 699 } 700 @GuardedBy("mLock") getSessionStateLocked(IBinder sessionToken, int callingUid, int userId)701 private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) { 702 UserState userState = getOrCreateUserStateLocked(userId); 703 return getSessionStateLocked(sessionToken, callingUid, userState); 704 } 705 706 @GuardedBy("mLock") getSessionStateLocked(IBinder sessionToken, int callingUid, UserState userState)707 private SessionState getSessionStateLocked(IBinder sessionToken, 708 int callingUid, UserState userState) { 709 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 710 if (sessionState == null) { 711 throw new SessionNotFoundException("Session state not found for token " + sessionToken); 712 } 713 // Only the application that requested this session or the system can access it. 714 if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) { 715 throw new SecurityException("Illegal access to the session with token " + sessionToken 716 + " from uid " + callingUid); 717 } 718 return sessionState; 719 } 720 721 @GuardedBy("mLock") getSessionLocked(IBinder sessionToken, int callingUid, int userId)722 private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) { 723 return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId)); 724 } 725 726 @GuardedBy("mLock") getSessionLocked(SessionState sessionState)727 private ITvInputSession getSessionLocked(SessionState sessionState) { 728 ITvInputSession session = sessionState.session; 729 if (session == null) { 730 throw new IllegalStateException("Session not yet created for token " 731 + sessionState.sessionToken); 732 } 733 return session; 734 } 735 resolveCallingUserId(int callingPid, int callingUid, int requestedUserId, String methodName)736 private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId, 737 String methodName) { 738 return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false, 739 false, methodName, null); 740 } 741 742 @GuardedBy("mLock") updateServiceConnectionLocked(ComponentName component, int userId)743 private void updateServiceConnectionLocked(ComponentName component, int userId) { 744 UserState userState = getOrCreateUserStateLocked(userId); 745 ServiceState serviceState = userState.serviceStateMap.get(component); 746 if (serviceState == null) { 747 return; 748 } 749 if (serviceState.reconnecting) { 750 if (!serviceState.sessionTokens.isEmpty()) { 751 // wait until all the sessions are removed. 752 return; 753 } 754 serviceState.reconnecting = false; 755 } 756 757 boolean shouldBind; 758 if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) { 759 shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware; 760 } else { 761 // For a non-current user, 762 // if sessionTokens is not empty, it contains recording sessions only 763 // because other sessions must have been removed while switching user 764 // and non-recording sessions are not created by createSession(). 765 shouldBind = !serviceState.sessionTokens.isEmpty(); 766 } 767 768 if (serviceState.service == null && shouldBind) { 769 // This means that the service is not yet connected but its state indicates that we 770 // have pending requests. Then, connect the service. 771 if (serviceState.bound) { 772 // We have already bound to the service so we don't try to bind again until after we 773 // unbind later on. 774 return; 775 } 776 if (DEBUG) { 777 Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")"); 778 } 779 780 Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component); 781 serviceState.bound = mContext.bindServiceAsUser( 782 i, serviceState.connection, 783 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, 784 new UserHandle(userId)); 785 } else if (serviceState.service != null && !shouldBind) { 786 // This means that the service is already connected but its state indicates that we have 787 // nothing to do with it. Then, disconnect the service. 788 if (DEBUG) { 789 Slog.d(TAG, "unbindService(service=" + component + ")"); 790 } 791 mContext.unbindService(serviceState.connection); 792 userState.serviceStateMap.remove(component); 793 } 794 } 795 796 @GuardedBy("mLock") abortPendingCreateSessionRequestsLocked(ServiceState serviceState, String inputId, int userId)797 private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState, 798 String inputId, int userId) { 799 // Let clients know the create session requests are failed. 800 UserState userState = getOrCreateUserStateLocked(userId); 801 List<SessionState> sessionsToAbort = new ArrayList<>(); 802 for (IBinder sessionToken : serviceState.sessionTokens) { 803 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 804 if (sessionState.session == null && (inputId == null 805 || sessionState.inputId.equals(inputId))) { 806 sessionsToAbort.add(sessionState); 807 } 808 } 809 for (SessionState sessionState : sessionsToAbort) { 810 removeSessionStateLocked(sessionState.sessionToken, sessionState.userId); 811 sendSessionTokenToClientLocked(sessionState.client, 812 sessionState.inputId, null, null, sessionState.seq); 813 } 814 updateServiceConnectionLocked(serviceState.component, userId); 815 } 816 817 @GuardedBy("mLock") createSessionInternalLocked( ITvInputService service, IBinder sessionToken, int userId)818 private boolean createSessionInternalLocked( 819 ITvInputService service, IBinder sessionToken, int userId) { 820 UserState userState = getOrCreateUserStateLocked(userId); 821 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 822 if (DEBUG) { 823 Slog.d(TAG, "createSessionInternalLocked(inputId=" 824 + sessionState.inputId + ", sessionId=" + sessionState.sessionId + ")"); 825 } 826 InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString()); 827 828 // Set up a callback to send the session token. 829 ITvInputSessionCallback callback = new SessionCallback(sessionState, channels); 830 831 boolean created = true; 832 // Create a session. When failed, send a null token immediately. 833 try { 834 if (sessionState.isRecordingSession) { 835 service.createRecordingSession( 836 callback, sessionState.inputId, sessionState.sessionId); 837 } else { 838 service.createSession(channels[1], callback, sessionState.inputId, 839 sessionState.sessionId, sessionState.tvAppAttributionSource); 840 } 841 } catch (RemoteException e) { 842 Slog.e(TAG, "error in createSession", e); 843 sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null, 844 null, sessionState.seq); 845 created = false; 846 } 847 channels[1].dispose(); 848 return created; 849 } 850 851 @GuardedBy("mLock") sendSessionTokenToClientLocked(ITvInputClient client, String inputId, IBinder sessionToken, InputChannel channel, int seq)852 private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId, 853 IBinder sessionToken, InputChannel channel, int seq) { 854 try { 855 client.onSessionCreated(inputId, sessionToken, channel, seq); 856 } catch (RemoteException e) { 857 Slog.e(TAG, "error in onSessionCreated", e); 858 } 859 } 860 861 @GuardedBy("mLock") 862 @Nullable releaseSessionLocked(IBinder sessionToken, int callingUid, int userId)863 private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { 864 SessionState sessionState = null; 865 try { 866 sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 867 UserState userState = getOrCreateUserStateLocked(userId); 868 if (sessionState.session != null) { 869 if (sessionToken == userState.mainSessionToken) { 870 setMainLocked(sessionToken, false, callingUid, userId); 871 } 872 sessionState.session.asBinder().unlinkToDeath(sessionState, 0); 873 sessionState.session.release(); 874 } 875 sessionState.currentChannel = null; 876 if (sessionState.isCurrent) { 877 sessionState.isCurrent = false; 878 notifyCurrentChannelInfosUpdatedLocked(userState); 879 } 880 } catch (RemoteException | SessionNotFoundException e) { 881 Slog.e(TAG, "error in releaseSession", e); 882 } finally { 883 if (sessionState != null) { 884 sessionState.session = null; 885 } 886 } 887 removeSessionStateLocked(sessionToken, userId); 888 return sessionState; 889 } 890 891 @GuardedBy("mLock") removeSessionStateLocked(IBinder sessionToken, int userId)892 private void removeSessionStateLocked(IBinder sessionToken, int userId) { 893 UserState userState = getOrCreateUserStateLocked(userId); 894 if (sessionToken == userState.mainSessionToken) { 895 if (DEBUG) { 896 Slog.d(TAG, "mainSessionToken=null"); 897 } 898 userState.mainSessionToken = null; 899 } 900 901 // Remove the session state from the global session state map of the current user. 902 SessionState sessionState = userState.sessionStateMap.remove(sessionToken); 903 904 if (sessionState == null) { 905 Slog.e(TAG, "sessionState null, no more remove session action!"); 906 return; 907 } 908 909 // Also remove the session token from the session token list of the current client and 910 // service. 911 ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder()); 912 if (clientState != null) { 913 clientState.sessionTokens.remove(sessionToken); 914 if (clientState.isEmpty()) { 915 userState.clientStateMap.remove(sessionState.client.asBinder()); 916 sessionState.client.asBinder().unlinkToDeath(clientState, 0); 917 } 918 } 919 920 mSessionIdToSessionStateMap.remove(sessionState.sessionId); 921 922 ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName); 923 if (serviceState != null) { 924 serviceState.sessionTokens.remove(sessionToken); 925 } 926 updateServiceConnectionLocked(sessionState.componentName, userId); 927 928 // Log the end of watch. 929 SomeArgs args = SomeArgs.obtain(); 930 args.arg1 = sessionToken; 931 args.arg2 = System.currentTimeMillis(); 932 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget(); 933 } 934 935 @GuardedBy("mLock") setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId)936 private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) { 937 try { 938 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 939 if (sessionState.hardwareSessionToken != null) { 940 sessionState = getSessionStateLocked(sessionState.hardwareSessionToken, 941 Process.SYSTEM_UID, userId); 942 } 943 ServiceState serviceState = getServiceStateLocked(sessionState.componentName, userId); 944 if (!serviceState.isHardware) { 945 return; 946 } 947 ITvInputSession session = getSessionLocked(sessionState); 948 session.setMain(isMain); 949 if (sessionState.isMainSession != isMain) { 950 UserState userState = getUserStateLocked(userId); 951 sessionState.isMainSession = isMain; 952 notifyCurrentChannelInfosUpdatedLocked(userState); 953 } 954 } catch (RemoteException | SessionNotFoundException e) { 955 Slog.e(TAG, "error in setMain", e); 956 } 957 } 958 959 @GuardedBy("mLock") notifyInputAddedLocked(UserState userState, String inputId)960 private void notifyInputAddedLocked(UserState userState, String inputId) { 961 if (DEBUG) { 962 Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")"); 963 } 964 int n = userState.mCallbacks.beginBroadcast(); 965 for (int i = 0; i < n; ++i) { 966 try { 967 userState.mCallbacks.getBroadcastItem(i).onInputAdded(inputId); 968 } catch (RemoteException e) { 969 Slog.e(TAG, "failed to report added input to callback", e); 970 } 971 } 972 userState.mCallbacks.finishBroadcast(); 973 } 974 975 @GuardedBy("mLock") notifyInputRemovedLocked(UserState userState, String inputId)976 private void notifyInputRemovedLocked(UserState userState, String inputId) { 977 if (DEBUG) { 978 Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")"); 979 } 980 int n = userState.mCallbacks.beginBroadcast(); 981 for (int i = 0; i < n; ++i) { 982 try { 983 userState.mCallbacks.getBroadcastItem(i).onInputRemoved(inputId); 984 } catch (RemoteException e) { 985 Slog.e(TAG, "failed to report removed input to callback", e); 986 } 987 } 988 userState.mCallbacks.finishBroadcast(); 989 } 990 991 @GuardedBy("mLock") notifyInputUpdatedLocked(UserState userState, String inputId)992 private void notifyInputUpdatedLocked(UserState userState, String inputId) { 993 if (DEBUG) { 994 Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")"); 995 } 996 int n = userState.mCallbacks.beginBroadcast(); 997 for (int i = 0; i < n; ++i) { 998 try { 999 userState.mCallbacks.getBroadcastItem(i).onInputUpdated(inputId); 1000 } catch (RemoteException e) { 1001 Slog.e(TAG, "failed to report updated input to callback", e); 1002 } 1003 } 1004 userState.mCallbacks.finishBroadcast(); 1005 } 1006 1007 @GuardedBy("mLock") notifyInputStateChangedLocked(UserState userState, String inputId, int state, ITvInputManagerCallback targetCallback)1008 private void notifyInputStateChangedLocked(UserState userState, String inputId, 1009 int state, ITvInputManagerCallback targetCallback) { 1010 if (DEBUG) { 1011 Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId 1012 + ", state=" + state + ")"); 1013 } 1014 if (targetCallback == null) { 1015 int n = userState.mCallbacks.beginBroadcast(); 1016 for (int i = 0; i < n; ++i) { 1017 try { 1018 userState.mCallbacks.getBroadcastItem(i).onInputStateChanged(inputId, state); 1019 } catch (RemoteException e) { 1020 Slog.e(TAG, "failed to report state change to callback", e); 1021 } 1022 } 1023 userState.mCallbacks.finishBroadcast(); 1024 } else { 1025 try { 1026 targetCallback.onInputStateChanged(inputId, state); 1027 } catch (RemoteException e) { 1028 Slog.e(TAG, "failed to report state change to callback", e); 1029 } 1030 } 1031 } 1032 1033 @GuardedBy("mLock") notifyCurrentChannelInfosUpdatedLocked(UserState userState)1034 private void notifyCurrentChannelInfosUpdatedLocked(UserState userState) { 1035 if (DEBUG) { 1036 Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked"); 1037 } 1038 int n = userState.mCallbacks.beginBroadcast(); 1039 for (int i = 0; i < n; ++i) { 1040 try { 1041 ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i); 1042 Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback); 1043 if (mContext.checkPermission(android.Manifest.permission.ACCESS_TUNED_INFO, 1044 pidUid.first, pidUid.second) != PackageManager.PERMISSION_GRANTED) { 1045 continue; 1046 } 1047 List<TunedInfo> infos = getCurrentTunedInfosInternalLocked( 1048 userState, pidUid.first, pidUid.second); 1049 callback.onCurrentTunedInfosUpdated(infos); 1050 } catch (RemoteException e) { 1051 Slog.e(TAG, "failed to report updated current channel infos to callback", e); 1052 } 1053 } 1054 userState.mCallbacks.finishBroadcast(); 1055 } 1056 1057 @GuardedBy("mLock") updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo)1058 private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) { 1059 if (DEBUG) { 1060 Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")"); 1061 } 1062 String inputId = inputInfo.getId(); 1063 TvInputState inputState = userState.inputMap.get(inputId); 1064 if (inputState == null) { 1065 Slog.e(TAG, "failed to set input info - unknown input id " + inputId); 1066 return; 1067 } 1068 inputState.info = inputInfo; 1069 inputState.uid = getInputUid(inputInfo); 1070 1071 int n = userState.mCallbacks.beginBroadcast(); 1072 for (int i = 0; i < n; ++i) { 1073 try { 1074 userState.mCallbacks.getBroadcastItem(i).onTvInputInfoUpdated(inputInfo); 1075 } catch (RemoteException e) { 1076 Slog.e(TAG, "failed to report updated input info to callback", e); 1077 } 1078 } 1079 userState.mCallbacks.finishBroadcast(); 1080 } 1081 1082 @GuardedBy("mLock") setStateLocked(String inputId, int state, int userId)1083 private void setStateLocked(String inputId, int state, int userId) { 1084 UserState userState = getOrCreateUserStateLocked(userId); 1085 TvInputState inputState = userState.inputMap.get(inputId); 1086 if (inputState == null) { 1087 Slog.e(TAG, "failed to setStateLocked - unknown input id " + inputId); 1088 return; 1089 } 1090 ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent()); 1091 int oldState = inputState.state; 1092 inputState.state = state; 1093 if (serviceState != null && serviceState.service == null 1094 && (!serviceState.sessionTokens.isEmpty() || serviceState.isHardware)) { 1095 // We don't notify state change while reconnecting. It should remain disconnected. 1096 return; 1097 } 1098 if (oldState != state) { 1099 notifyInputStateChangedLocked(userState, inputId, state, null); 1100 } 1101 } 1102 1103 private final class BinderService extends ITvInputManager.Stub { 1104 @Override getTvInputList(int userId)1105 public List<TvInputInfo> getTvInputList(int userId) { 1106 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1107 Binder.getCallingUid(), userId, "getTvInputList"); 1108 final long identity = Binder.clearCallingIdentity(); 1109 try { 1110 synchronized (mLock) { 1111 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1112 List<TvInputInfo> inputList = new ArrayList<>(); 1113 for (TvInputState state : userState.inputMap.values()) { 1114 inputList.add(state.info); 1115 } 1116 return inputList; 1117 } 1118 } finally { 1119 Binder.restoreCallingIdentity(identity); 1120 } 1121 } 1122 1123 @Override getTvInputInfo(String inputId, int userId)1124 public TvInputInfo getTvInputInfo(String inputId, int userId) { 1125 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1126 Binder.getCallingUid(), userId, "getTvInputInfo"); 1127 final long identity = Binder.clearCallingIdentity(); 1128 try { 1129 synchronized (mLock) { 1130 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1131 TvInputState state = userState.inputMap.get(inputId); 1132 return state == null ? null : state.info; 1133 } 1134 } finally { 1135 Binder.restoreCallingIdentity(identity); 1136 } 1137 } 1138 updateTvInputInfo(TvInputInfo inputInfo, int userId)1139 public void updateTvInputInfo(TvInputInfo inputInfo, int userId) { 1140 String inputInfoPackageName = inputInfo.getServiceInfo().packageName; 1141 String callingPackageName = getCallingPackageName(); 1142 if (!TextUtils.equals(inputInfoPackageName, callingPackageName) 1143 && mContext.checkCallingPermission( 1144 android.Manifest.permission.WRITE_SECURE_SETTINGS) 1145 != PackageManager.PERMISSION_GRANTED) { 1146 // Only the app owning the input and system settings are allowed to update info. 1147 throw new IllegalArgumentException("calling package " + callingPackageName 1148 + " is not allowed to change TvInputInfo for " + inputInfoPackageName); 1149 } 1150 1151 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1152 Binder.getCallingUid(), userId, "updateTvInputInfo"); 1153 final long identity = Binder.clearCallingIdentity(); 1154 try { 1155 synchronized (mLock) { 1156 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1157 updateTvInputInfoLocked(userState, inputInfo); 1158 } 1159 } finally { 1160 Binder.restoreCallingIdentity(identity); 1161 } 1162 } 1163 getCallingPackageName()1164 private String getCallingPackageName() { 1165 final String[] packages = mContext.getPackageManager().getPackagesForUid( 1166 Binder.getCallingUid()); 1167 if (packages != null && packages.length > 0) { 1168 return packages[0]; 1169 } 1170 return "unknown"; 1171 } 1172 1173 @Override getTvInputState(String inputId, int userId)1174 public int getTvInputState(String inputId, int userId) { 1175 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1176 Binder.getCallingUid(), userId, "getTvInputState"); 1177 final long identity = Binder.clearCallingIdentity(); 1178 try { 1179 synchronized (mLock) { 1180 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1181 TvInputState state = userState.inputMap.get(inputId); 1182 return state == null ? INPUT_STATE_CONNECTED : state.state; 1183 } 1184 } finally { 1185 Binder.restoreCallingIdentity(identity); 1186 } 1187 } 1188 1189 @Override getAvailableExtensionInterfaceNames(String inputId, int userId)1190 public List<String> getAvailableExtensionInterfaceNames(String inputId, int userId) { 1191 ensureTisExtensionInterfacePermission(); 1192 final int callingUid = Binder.getCallingUid(); 1193 final int callingPid = Binder.getCallingPid(); 1194 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, 1195 userId, "getAvailableExtensionInterfaceNames"); 1196 final long identity = Binder.clearCallingIdentity(); 1197 try { 1198 ITvInputService service = null; 1199 synchronized (mLock) { 1200 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1201 TvInputState inputState = userState.inputMap.get(inputId); 1202 if (inputState != null) { 1203 ServiceState serviceState = 1204 userState.serviceStateMap.get(inputState.info.getComponent()); 1205 if (serviceState != null && serviceState.isHardware 1206 && serviceState.service != null) { 1207 service = serviceState.service; 1208 } 1209 } 1210 } 1211 try { 1212 if (service != null) { 1213 List<String> interfaces = new ArrayList<>(); 1214 for (final String name : CollectionUtils.emptyIfNull( 1215 service.getAvailableExtensionInterfaceNames())) { 1216 String permission = service.getExtensionInterfacePermission(name); 1217 if (permission == null 1218 || mContext.checkPermission(permission, callingPid, callingUid) 1219 == PackageManager.PERMISSION_GRANTED) { 1220 interfaces.add(name); 1221 } 1222 } 1223 return interfaces; 1224 } 1225 } catch (RemoteException e) { 1226 Slog.e(TAG, "error in getAvailableExtensionInterfaceNames " 1227 + "or getExtensionInterfacePermission", e); 1228 } 1229 return new ArrayList<>(); 1230 } finally { 1231 Binder.restoreCallingIdentity(identity); 1232 } 1233 } 1234 1235 @Override getExtensionInterface(String inputId, String name, int userId)1236 public IBinder getExtensionInterface(String inputId, String name, int userId) { 1237 ensureTisExtensionInterfacePermission(); 1238 final int callingUid = Binder.getCallingUid(); 1239 final int callingPid = Binder.getCallingPid(); 1240 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, 1241 userId, "getExtensionInterface"); 1242 final long identity = Binder.clearCallingIdentity(); 1243 try { 1244 ITvInputService service = null; 1245 synchronized (mLock) { 1246 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1247 TvInputState inputState = userState.inputMap.get(inputId); 1248 if (inputState != null) { 1249 ServiceState serviceState = 1250 userState.serviceStateMap.get(inputState.info.getComponent()); 1251 if (serviceState != null && serviceState.isHardware 1252 && serviceState.service != null) { 1253 service = serviceState.service; 1254 } 1255 } 1256 } 1257 try { 1258 if (service != null) { 1259 String permission = service.getExtensionInterfacePermission(name); 1260 if (permission == null 1261 || mContext.checkPermission(permission, callingPid, callingUid) 1262 == PackageManager.PERMISSION_GRANTED) { 1263 return service.getExtensionInterface(name); 1264 } 1265 } 1266 } catch (RemoteException e) { 1267 Slog.e(TAG, "error in getExtensionInterfacePermission " 1268 + "or getExtensionInterface", e); 1269 } 1270 return null; 1271 } finally { 1272 Binder.restoreCallingIdentity(identity); 1273 } 1274 } 1275 1276 @Override getTvContentRatingSystemList(int userId)1277 public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) { 1278 if (mContext.checkCallingPermission( 1279 android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) 1280 != PackageManager.PERMISSION_GRANTED) { 1281 throw new SecurityException( 1282 "The caller does not have permission to read content rating systems"); 1283 } 1284 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1285 Binder.getCallingUid(), userId, "getTvContentRatingSystemList"); 1286 final long identity = Binder.clearCallingIdentity(); 1287 try { 1288 synchronized (mLock) { 1289 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1290 return userState.contentRatingSystemList; 1291 } 1292 } finally { 1293 Binder.restoreCallingIdentity(identity); 1294 } 1295 } 1296 1297 @Override sendTvInputNotifyIntent(Intent intent, int userId)1298 public void sendTvInputNotifyIntent(Intent intent, int userId) { 1299 if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) 1300 != PackageManager.PERMISSION_GRANTED) { 1301 throw new SecurityException("The caller: " + getCallingPackageName() 1302 + " doesn't have permission: " 1303 + android.Manifest.permission.NOTIFY_TV_INPUTS); 1304 } 1305 if (TextUtils.isEmpty(intent.getPackage())) { 1306 throw new IllegalArgumentException("Must specify package name to notify."); 1307 } 1308 switch (intent.getAction()) { 1309 case TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED: 1310 if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) { 1311 throw new IllegalArgumentException("Invalid preview program ID."); 1312 } 1313 break; 1314 case TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED: 1315 if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) { 1316 throw new IllegalArgumentException("Invalid watch next program ID."); 1317 } 1318 break; 1319 case TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT: 1320 if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) { 1321 throw new IllegalArgumentException("Invalid preview program ID."); 1322 } 1323 if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) { 1324 throw new IllegalArgumentException("Invalid watch next program ID."); 1325 } 1326 break; 1327 default: 1328 throw new IllegalArgumentException("Invalid TV input notifying action: " 1329 + intent.getAction()); 1330 } 1331 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1332 Binder.getCallingUid(), userId, "sendTvInputNotifyIntent"); 1333 final long identity = Binder.clearCallingIdentity(); 1334 try { 1335 getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId)); 1336 } finally { 1337 Binder.restoreCallingIdentity(identity); 1338 } 1339 } 1340 1341 @Override registerCallback(final ITvInputManagerCallback callback, int userId)1342 public void registerCallback(final ITvInputManagerCallback callback, int userId) { 1343 int callingPid = Binder.getCallingPid(); 1344 int callingUid = Binder.getCallingUid(); 1345 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, 1346 "registerCallback"); 1347 final long identity = Binder.clearCallingIdentity(); 1348 try { 1349 synchronized (mLock) { 1350 final UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1351 if (!userState.mCallbacks.register(callback)) { 1352 Slog.e(TAG, "client process has already died"); 1353 } else { 1354 userState.callbackPidUidMap.put( 1355 callback, Pair.create(callingPid, callingUid)); 1356 } 1357 } 1358 } finally { 1359 Binder.restoreCallingIdentity(identity); 1360 } 1361 } 1362 1363 @Override unregisterCallback(ITvInputManagerCallback callback, int userId)1364 public void unregisterCallback(ITvInputManagerCallback callback, int userId) { 1365 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1366 Binder.getCallingUid(), userId, "unregisterCallback"); 1367 final long identity = Binder.clearCallingIdentity(); 1368 try { 1369 synchronized (mLock) { 1370 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1371 userState.mCallbacks.unregister(callback); 1372 userState.callbackPidUidMap.remove(callback); 1373 } 1374 } finally { 1375 Binder.restoreCallingIdentity(identity); 1376 } 1377 } 1378 1379 @Override isParentalControlsEnabled(int userId)1380 public boolean isParentalControlsEnabled(int userId) { 1381 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1382 Binder.getCallingUid(), userId, "isParentalControlsEnabled"); 1383 final long identity = Binder.clearCallingIdentity(); 1384 try { 1385 synchronized (mLock) { 1386 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1387 return userState.persistentDataStore.isParentalControlsEnabled(); 1388 } 1389 } finally { 1390 Binder.restoreCallingIdentity(identity); 1391 } 1392 } 1393 1394 @Override setParentalControlsEnabled(boolean enabled, int userId)1395 public void setParentalControlsEnabled(boolean enabled, int userId) { 1396 ensureParentalControlsPermission(); 1397 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1398 Binder.getCallingUid(), userId, "setParentalControlsEnabled"); 1399 final long identity = Binder.clearCallingIdentity(); 1400 try { 1401 synchronized (mLock) { 1402 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1403 userState.persistentDataStore.setParentalControlsEnabled(enabled); 1404 } 1405 } finally { 1406 Binder.restoreCallingIdentity(identity); 1407 } 1408 } 1409 1410 @Override isRatingBlocked(String rating, int userId)1411 public boolean isRatingBlocked(String rating, int userId) { 1412 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1413 Binder.getCallingUid(), userId, "isRatingBlocked"); 1414 final long identity = Binder.clearCallingIdentity(); 1415 try { 1416 synchronized (mLock) { 1417 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1418 return userState.persistentDataStore.isRatingBlocked( 1419 TvContentRating.unflattenFromString(rating)); 1420 } 1421 } finally { 1422 Binder.restoreCallingIdentity(identity); 1423 } 1424 } 1425 1426 @Override getBlockedRatings(int userId)1427 public List<String> getBlockedRatings(int userId) { 1428 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1429 Binder.getCallingUid(), userId, "getBlockedRatings"); 1430 final long identity = Binder.clearCallingIdentity(); 1431 try { 1432 synchronized (mLock) { 1433 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1434 List<String> ratings = new ArrayList<>(); 1435 for (TvContentRating rating 1436 : userState.persistentDataStore.getBlockedRatings()) { 1437 ratings.add(rating.flattenToString()); 1438 } 1439 return ratings; 1440 } 1441 } finally { 1442 Binder.restoreCallingIdentity(identity); 1443 } 1444 } 1445 1446 @Override addBlockedRating(String rating, int userId)1447 public void addBlockedRating(String rating, int userId) { 1448 ensureParentalControlsPermission(); 1449 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1450 Binder.getCallingUid(), userId, "addBlockedRating"); 1451 final long identity = Binder.clearCallingIdentity(); 1452 try { 1453 synchronized (mLock) { 1454 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1455 userState.persistentDataStore.addBlockedRating( 1456 TvContentRating.unflattenFromString(rating)); 1457 } 1458 } finally { 1459 Binder.restoreCallingIdentity(identity); 1460 } 1461 } 1462 1463 @Override removeBlockedRating(String rating, int userId)1464 public void removeBlockedRating(String rating, int userId) { 1465 ensureParentalControlsPermission(); 1466 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 1467 Binder.getCallingUid(), userId, "removeBlockedRating"); 1468 final long identity = Binder.clearCallingIdentity(); 1469 try { 1470 synchronized (mLock) { 1471 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1472 userState.persistentDataStore.removeBlockedRating( 1473 TvContentRating.unflattenFromString(rating)); 1474 } 1475 } finally { 1476 Binder.restoreCallingIdentity(identity); 1477 } 1478 } 1479 ensureParentalControlsPermission()1480 private void ensureParentalControlsPermission() { 1481 if (mContext.checkCallingPermission( 1482 android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) 1483 != PackageManager.PERMISSION_GRANTED) { 1484 throw new SecurityException( 1485 "The caller does not have parental controls permission"); 1486 } 1487 } 1488 1489 @Override createSession(final ITvInputClient client, final String inputId, AttributionSource tvAppAttributionSource, boolean isRecordingSession, int seq, int userId)1490 public void createSession(final ITvInputClient client, final String inputId, 1491 AttributionSource tvAppAttributionSource, boolean isRecordingSession, int seq, 1492 int userId) { 1493 final int callingUid = Binder.getCallingUid(); 1494 final int callingPid = Binder.getCallingPid(); 1495 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, 1496 userId, "createSession"); 1497 final long identity = Binder.clearCallingIdentity(); 1498 /** 1499 * A randomly generated id for this this session. 1500 * 1501 * <p>This field contains no user or device reference and is large enough to be 1502 * effectively globally unique. 1503 * 1504 * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. 1505 * Inspect the code at: 1506 * 1507 * <ul> 1508 * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState 1509 * <li>{@link #logTuneStateChanged} 1510 * <li>{@link TvInputManagerService.BinderService#createSession} 1511 * <li>{@link SessionState#sessionId} 1512 * </ul> 1513 */ 1514 String uniqueSessionId = UUID.randomUUID().toString(); 1515 try { 1516 synchronized (mLock) { 1517 if (userId != mCurrentUserId && !mRunningProfiles.contains(userId) 1518 && !isRecordingSession) { 1519 // Only current user and its running profiles can create 1520 // non-recording sessions. 1521 // Let the client get onConnectionFailed callback for this case. 1522 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 1523 return; 1524 } 1525 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1526 TvInputState inputState = userState.inputMap.get(inputId); 1527 if (inputState == null) { 1528 Slog.w(TAG, "Failed to find input state for inputId=" + inputId); 1529 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 1530 return; 1531 } 1532 TvInputInfo info = inputState.info; 1533 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 1534 if (serviceState == null) { 1535 int tisUid = PackageManager.getApplicationInfoAsUserCached( 1536 info.getComponent().getPackageName(), 0, resolvedUserId).uid; 1537 serviceState = new ServiceState(info.getComponent(), resolvedUserId); 1538 userState.serviceStateMap.put(info.getComponent(), serviceState); 1539 } 1540 // Send a null token immediately while reconnecting. 1541 if (serviceState.reconnecting) { 1542 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 1543 return; 1544 } 1545 1546 // Create a new session token and a session state. 1547 IBinder sessionToken = new Binder(); 1548 SessionState sessionState = new SessionState(sessionToken, info.getId(), 1549 info.getComponent(), isRecordingSession, client, seq, callingUid, 1550 callingPid, resolvedUserId, uniqueSessionId, tvAppAttributionSource); 1551 1552 // Add them to the global session state map of the current user. 1553 userState.sessionStateMap.put(sessionToken, sessionState); 1554 1555 // Map the session id to the sessionStateMap in the user state 1556 mSessionIdToSessionStateMap.put(uniqueSessionId, sessionState); 1557 1558 // Also, add them to the session state map of the current service. 1559 serviceState.sessionTokens.add(sessionToken); 1560 1561 if (serviceState.service != null) { 1562 if (!createSessionInternalLocked( 1563 serviceState.service, sessionToken, resolvedUserId)) { 1564 removeSessionStateLocked(sessionToken, resolvedUserId); 1565 } 1566 } else { 1567 updateServiceConnectionLocked(info.getComponent(), resolvedUserId); 1568 } 1569 logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED, 1570 sessionState, inputState); 1571 } 1572 } finally { 1573 Binder.restoreCallingIdentity(identity); 1574 } 1575 } 1576 1577 @Override releaseSession(IBinder sessionToken, int userId)1578 public void releaseSession(IBinder sessionToken, int userId) { 1579 if (DEBUG) { 1580 Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")"); 1581 } 1582 final int callingUid = Binder.getCallingUid(); 1583 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1584 userId, "releaseSession"); 1585 final long identity = Binder.clearCallingIdentity(); 1586 try { 1587 SessionState sessionState = null; 1588 UserState userState = null; 1589 synchronized (mLock) { 1590 sessionState = releaseSessionLocked(sessionToken, callingUid, resolvedUserId); 1591 userState = getUserStateLocked(userId); 1592 } 1593 if (sessionState != null) { 1594 TvInputState tvInputState = TvInputManagerService.getTvInputState(sessionState, 1595 userState); 1596 logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__RELEASED, 1597 sessionState, tvInputState); 1598 } 1599 } finally { 1600 Binder.restoreCallingIdentity(identity); 1601 } 1602 } 1603 1604 @Override setMainSession(IBinder sessionToken, int userId)1605 public void setMainSession(IBinder sessionToken, int userId) { 1606 if (mContext.checkCallingPermission( 1607 android.Manifest.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE) 1608 != PackageManager.PERMISSION_GRANTED) { 1609 throw new SecurityException( 1610 "The caller does not have CHANGE_HDMI_CEC_ACTIVE_SOURCE permission"); 1611 } 1612 if (DEBUG) { 1613 Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")"); 1614 } 1615 final int callingUid = Binder.getCallingUid(); 1616 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1617 userId, "setMainSession"); 1618 final long identity = Binder.clearCallingIdentity(); 1619 try { 1620 synchronized (mLock) { 1621 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1622 if (userState.mainSessionToken == sessionToken) { 1623 return; 1624 } 1625 if (DEBUG) { 1626 Slog.d(TAG, "mainSessionToken=" + sessionToken); 1627 } 1628 IBinder oldMainSessionToken = userState.mainSessionToken; 1629 userState.mainSessionToken = sessionToken; 1630 1631 // Inform the new main session first. 1632 // See {@link TvInputService.Session#onSetMain}. 1633 if (sessionToken != null) { 1634 setMainLocked(sessionToken, true, callingUid, userId); 1635 } 1636 if (oldMainSessionToken != null) { 1637 setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId); 1638 } 1639 } 1640 } finally { 1641 Binder.restoreCallingIdentity(identity); 1642 } 1643 } 1644 1645 @Override setSurface(IBinder sessionToken, Surface surface, int userId)1646 public void setSurface(IBinder sessionToken, Surface surface, int userId) { 1647 final int callingUid = Binder.getCallingUid(); 1648 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1649 userId, "setSurface"); 1650 final long identity = Binder.clearCallingIdentity(); 1651 SessionState sessionState = null; 1652 UserState userState = null; 1653 try { 1654 synchronized (mLock) { 1655 try { 1656 userState = getUserStateLocked(userId); 1657 sessionState = getSessionStateLocked(sessionToken, callingUid, 1658 resolvedUserId); 1659 if (sessionState.hardwareSessionToken == null) { 1660 getSessionLocked(sessionState).setSurface(surface); 1661 } else { 1662 getSessionLocked(sessionState.hardwareSessionToken, 1663 Process.SYSTEM_UID, resolvedUserId).setSurface(surface); 1664 } 1665 boolean isVisible = (surface == null); 1666 if (sessionState.isVisible != isVisible) { 1667 sessionState.isVisible = isVisible; 1668 notifyCurrentChannelInfosUpdatedLocked(userState); 1669 } 1670 } catch (RemoteException | SessionNotFoundException e) { 1671 Slog.e(TAG, "error in setSurface", e); 1672 } 1673 } 1674 } finally { 1675 if (surface != null) { 1676 // surface is not used in TvInputManagerService. 1677 surface.release(); 1678 } 1679 if (sessionState != null) { 1680 int state = surface == null 1681 ? 1682 FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED 1683 : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED; 1684 logTuneStateChanged(state, sessionState, 1685 TvInputManagerService.getTvInputState(sessionState, userState)); 1686 } 1687 Binder.restoreCallingIdentity(identity); 1688 } 1689 } 1690 1691 @Override dispatchSurfaceChanged(IBinder sessionToken, int format, int width, int height, int userId)1692 public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width, 1693 int height, int userId) { 1694 final int callingUid = Binder.getCallingUid(); 1695 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1696 userId, "dispatchSurfaceChanged"); 1697 final long identity = Binder.clearCallingIdentity(); 1698 try { 1699 synchronized (mLock) { 1700 try { 1701 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1702 resolvedUserId); 1703 getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, 1704 height); 1705 if (sessionState.hardwareSessionToken != null) { 1706 getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID, 1707 resolvedUserId).dispatchSurfaceChanged(format, width, height); 1708 } 1709 } catch (RemoteException | SessionNotFoundException e) { 1710 Slog.e(TAG, "error in dispatchSurfaceChanged", e); 1711 } 1712 } 1713 } finally { 1714 Binder.restoreCallingIdentity(identity); 1715 } 1716 } 1717 1718 @Override setVolume(IBinder sessionToken, float volume, int userId)1719 public void setVolume(IBinder sessionToken, float volume, int userId) { 1720 final float REMOTE_VOLUME_ON = 1.0f; 1721 final float REMOTE_VOLUME_OFF = 0f; 1722 final int callingUid = Binder.getCallingUid(); 1723 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1724 userId, "setVolume"); 1725 final long identity = Binder.clearCallingIdentity(); 1726 try { 1727 synchronized (mLock) { 1728 try { 1729 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1730 resolvedUserId); 1731 getSessionLocked(sessionState).setVolume(volume); 1732 if (sessionState.hardwareSessionToken != null) { 1733 // Here, we let the hardware session know only whether volume is on or 1734 // off to prevent that the volume is controlled in the both side. 1735 getSessionLocked(sessionState.hardwareSessionToken, 1736 Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f) 1737 ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF); 1738 } 1739 } catch (RemoteException | SessionNotFoundException e) { 1740 Slog.e(TAG, "error in setVolume", e); 1741 } 1742 } 1743 } finally { 1744 Binder.restoreCallingIdentity(identity); 1745 } 1746 } 1747 1748 @Override tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId)1749 public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) { 1750 final int callingUid = Binder.getCallingUid(); 1751 final int callingPid = Binder.getCallingPid(); 1752 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "tune"); 1753 final long identity = Binder.clearCallingIdentity(); 1754 try { 1755 synchronized (mLock) { 1756 try { 1757 getSessionLocked(sessionToken, callingUid, resolvedUserId).tune( 1758 channelUri, params); 1759 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 1760 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1761 userState); 1762 if (!sessionState.isCurrent 1763 || !Objects.equals(sessionState.currentChannel, channelUri)) { 1764 sessionState.isCurrent = true; 1765 sessionState.currentChannel = channelUri; 1766 notifyCurrentChannelInfosUpdatedLocked(userState); 1767 } 1768 if (TvContract.isChannelUriForPassthroughInput(channelUri)) { 1769 // Do not log the watch history for passthrough inputs. 1770 return; 1771 } 1772 1773 if (sessionState.isRecordingSession) { 1774 return; 1775 } 1776 1777 logTuneStateChanged( 1778 FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED, 1779 sessionState, 1780 TvInputManagerService.getTvInputState(sessionState, userState)); 1781 // Log the start of watch. 1782 SomeArgs args = SomeArgs.obtain(); 1783 args.arg1 = sessionState.componentName.getPackageName(); 1784 args.arg2 = System.currentTimeMillis(); 1785 args.arg3 = ContentUris.parseId(channelUri); 1786 args.arg4 = params; 1787 args.arg5 = sessionToken; 1788 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args) 1789 .sendToTarget(); 1790 } catch (RemoteException | SessionNotFoundException e) { 1791 Slog.e(TAG, "error in tune", e); 1792 } 1793 } 1794 } finally { 1795 Binder.restoreCallingIdentity(identity); 1796 } 1797 } 1798 1799 @Override unblockContent( IBinder sessionToken, String unblockedRating, int userId)1800 public void unblockContent( 1801 IBinder sessionToken, String unblockedRating, int userId) { 1802 ensureParentalControlsPermission(); 1803 final int callingUid = Binder.getCallingUid(); 1804 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1805 userId, "unblockContent"); 1806 final long identity = Binder.clearCallingIdentity(); 1807 try { 1808 synchronized (mLock) { 1809 try { 1810 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1811 .unblockContent(unblockedRating); 1812 } catch (RemoteException | SessionNotFoundException e) { 1813 Slog.e(TAG, "error in unblockContent", e); 1814 } 1815 } 1816 } finally { 1817 Binder.restoreCallingIdentity(identity); 1818 } 1819 } 1820 1821 @Override setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId)1822 public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) { 1823 final int callingUid = Binder.getCallingUid(); 1824 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1825 userId, "setCaptionEnabled"); 1826 final long identity = Binder.clearCallingIdentity(); 1827 try { 1828 synchronized (mLock) { 1829 try { 1830 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1831 .setCaptionEnabled(enabled); 1832 } catch (RemoteException | SessionNotFoundException e) { 1833 Slog.e(TAG, "error in setCaptionEnabled", e); 1834 } 1835 } 1836 } finally { 1837 Binder.restoreCallingIdentity(identity); 1838 } 1839 } 1840 1841 @Override selectAudioPresentation(IBinder sessionToken, int presentationId, int programId, int userId)1842 public void selectAudioPresentation(IBinder sessionToken, int presentationId, 1843 int programId, int userId) { 1844 final int callingUid = Binder.getCallingUid(); 1845 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1846 userId, "selectAudioPresentation"); 1847 final long identity = Binder.clearCallingIdentity(); 1848 try { 1849 synchronized (mLock) { 1850 try { 1851 getSessionLocked(sessionToken, callingUid, 1852 resolvedUserId).selectAudioPresentation( 1853 presentationId, programId); 1854 } catch (RemoteException | SessionNotFoundException e) { 1855 Slog.e(TAG, "error in selectAudioPresentation", e); 1856 } 1857 } 1858 } finally { 1859 Binder.restoreCallingIdentity(identity); 1860 } 1861 } 1862 1863 @Override selectTrack(IBinder sessionToken, int type, String trackId, int userId)1864 public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) { 1865 final int callingUid = Binder.getCallingUid(); 1866 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1867 userId, "selectTrack"); 1868 final long identity = Binder.clearCallingIdentity(); 1869 try { 1870 synchronized (mLock) { 1871 try { 1872 getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack( 1873 type, trackId); 1874 } catch (RemoteException | SessionNotFoundException e) { 1875 Slog.e(TAG, "error in selectTrack", e); 1876 } 1877 } 1878 } finally { 1879 Binder.restoreCallingIdentity(identity); 1880 } 1881 } 1882 1883 @Override setInteractiveAppNotificationEnabled( IBinder sessionToken, boolean enabled, int userId)1884 public void setInteractiveAppNotificationEnabled( 1885 IBinder sessionToken, boolean enabled, int userId) { 1886 final int callingUid = Binder.getCallingUid(); 1887 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1888 userId, "setInteractiveAppNotificationEnabled"); 1889 final long identity = Binder.clearCallingIdentity(); 1890 try { 1891 synchronized (mLock) { 1892 try { 1893 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1894 .setInteractiveAppNotificationEnabled(enabled); 1895 } catch (RemoteException | SessionNotFoundException e) { 1896 Slog.e(TAG, "error in setInteractiveAppNotificationEnabled", e); 1897 } 1898 } 1899 } finally { 1900 Binder.restoreCallingIdentity(identity); 1901 } 1902 } 1903 1904 @Override sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data, int userId)1905 public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data, 1906 int userId) { 1907 final int callingUid = Binder.getCallingUid(); 1908 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1909 userId, "sendAppPrivateCommand"); 1910 final long identity = Binder.clearCallingIdentity(); 1911 try { 1912 synchronized (mLock) { 1913 try { 1914 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1915 .appPrivateCommand(command, data); 1916 } catch (RemoteException | SessionNotFoundException e) { 1917 Slog.e(TAG, "error in appPrivateCommand", e); 1918 } 1919 } 1920 } finally { 1921 Binder.restoreCallingIdentity(identity); 1922 } 1923 } 1924 1925 @Override createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame, int userId)1926 public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame, 1927 int userId) { 1928 final int callingUid = Binder.getCallingUid(); 1929 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1930 userId, "createOverlayView"); 1931 final long identity = Binder.clearCallingIdentity(); 1932 try { 1933 synchronized (mLock) { 1934 try { 1935 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1936 .createOverlayView(windowToken, frame); 1937 } catch (RemoteException | SessionNotFoundException e) { 1938 Slog.e(TAG, "error in createOverlayView", e); 1939 } 1940 } 1941 } finally { 1942 Binder.restoreCallingIdentity(identity); 1943 } 1944 } 1945 1946 @Override relayoutOverlayView(IBinder sessionToken, Rect frame, int userId)1947 public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) { 1948 final int callingUid = Binder.getCallingUid(); 1949 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1950 userId, "relayoutOverlayView"); 1951 final long identity = Binder.clearCallingIdentity(); 1952 try { 1953 synchronized (mLock) { 1954 try { 1955 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1956 .relayoutOverlayView(frame); 1957 } catch (RemoteException | SessionNotFoundException e) { 1958 Slog.e(TAG, "error in relayoutOverlayView", e); 1959 } 1960 } 1961 } finally { 1962 Binder.restoreCallingIdentity(identity); 1963 } 1964 } 1965 1966 @Override removeOverlayView(IBinder sessionToken, int userId)1967 public void removeOverlayView(IBinder sessionToken, int userId) { 1968 final int callingUid = Binder.getCallingUid(); 1969 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1970 userId, "removeOverlayView"); 1971 final long identity = Binder.clearCallingIdentity(); 1972 try { 1973 synchronized (mLock) { 1974 try { 1975 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1976 .removeOverlayView(); 1977 } catch (RemoteException | SessionNotFoundException e) { 1978 Slog.e(TAG, "error in removeOverlayView", e); 1979 } 1980 } 1981 } finally { 1982 Binder.restoreCallingIdentity(identity); 1983 } 1984 } 1985 1986 @Override timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId)1987 public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) { 1988 final int callingUid = Binder.getCallingUid(); 1989 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1990 userId, "timeShiftPlay"); 1991 final long identity = Binder.clearCallingIdentity(); 1992 try { 1993 synchronized (mLock) { 1994 try { 1995 getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay( 1996 recordedProgramUri); 1997 } catch (RemoteException | SessionNotFoundException e) { 1998 Slog.e(TAG, "error in timeShiftPlay", e); 1999 } 2000 } 2001 } finally { 2002 Binder.restoreCallingIdentity(identity); 2003 } 2004 } 2005 2006 @Override timeShiftPause(IBinder sessionToken, int userId)2007 public void timeShiftPause(IBinder sessionToken, int userId) { 2008 final int callingUid = Binder.getCallingUid(); 2009 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2010 userId, "timeShiftPause"); 2011 final long identity = Binder.clearCallingIdentity(); 2012 try { 2013 synchronized (mLock) { 2014 try { 2015 getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause(); 2016 } catch (RemoteException | SessionNotFoundException e) { 2017 Slog.e(TAG, "error in timeShiftPause", e); 2018 } 2019 } 2020 } finally { 2021 Binder.restoreCallingIdentity(identity); 2022 } 2023 } 2024 2025 @Override timeShiftResume(IBinder sessionToken, int userId)2026 public void timeShiftResume(IBinder sessionToken, int userId) { 2027 final int callingUid = Binder.getCallingUid(); 2028 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2029 userId, "timeShiftResume"); 2030 final long identity = Binder.clearCallingIdentity(); 2031 try { 2032 synchronized (mLock) { 2033 try { 2034 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2035 .timeShiftResume(); 2036 } catch (RemoteException | SessionNotFoundException e) { 2037 Slog.e(TAG, "error in timeShiftResume", e); 2038 } 2039 } 2040 } finally { 2041 Binder.restoreCallingIdentity(identity); 2042 } 2043 } 2044 2045 @Override timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId)2046 public void timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId) { 2047 final int callingUid = Binder.getCallingUid(); 2048 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2049 userId, "timeShiftSeekTo"); 2050 final long identity = Binder.clearCallingIdentity(); 2051 try { 2052 synchronized (mLock) { 2053 try { 2054 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2055 .timeShiftSeekTo(timeMs); 2056 } catch (RemoteException | SessionNotFoundException e) { 2057 Slog.e(TAG, "error in timeShiftSeekTo", e); 2058 } 2059 } 2060 } finally { 2061 Binder.restoreCallingIdentity(identity); 2062 } 2063 } 2064 2065 @Override timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params, int userId)2066 public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params, 2067 int userId) { 2068 final int callingUid = Binder.getCallingUid(); 2069 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2070 userId, "timeShiftSetPlaybackParams"); 2071 final long identity = Binder.clearCallingIdentity(); 2072 try { 2073 synchronized (mLock) { 2074 try { 2075 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2076 .timeShiftSetPlaybackParams(params); 2077 } catch (RemoteException | SessionNotFoundException e) { 2078 Slog.e(TAG, "error in timeShiftSetPlaybackParams", e); 2079 } 2080 } 2081 } finally { 2082 Binder.restoreCallingIdentity(identity); 2083 } 2084 } 2085 2086 @Override timeShiftSetMode(IBinder sessionToken, int mode, int userId)2087 public void timeShiftSetMode(IBinder sessionToken, int mode, int userId) { 2088 final int callingUid = Binder.getCallingUid(); 2089 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2090 userId, "timeShiftSetMode"); 2091 final long identity = Binder.clearCallingIdentity(); 2092 try { 2093 synchronized (mLock) { 2094 try { 2095 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2096 .timeShiftSetMode(mode); 2097 } catch (RemoteException | SessionNotFoundException e) { 2098 Slog.e(TAG, "error in timeShiftSetMode", e); 2099 } 2100 } 2101 } finally { 2102 Binder.restoreCallingIdentity(identity); 2103 } 2104 } 2105 2106 @Override timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable, int userId)2107 public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable, 2108 int userId) { 2109 final int callingUid = Binder.getCallingUid(); 2110 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2111 userId, "timeShiftEnablePositionTracking"); 2112 final long identity = Binder.clearCallingIdentity(); 2113 try { 2114 synchronized (mLock) { 2115 try { 2116 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2117 .timeShiftEnablePositionTracking(enable); 2118 } catch (RemoteException | SessionNotFoundException e) { 2119 Slog.e(TAG, "error in timeShiftEnablePositionTracking", e); 2120 } 2121 } 2122 } finally { 2123 Binder.restoreCallingIdentity(identity); 2124 } 2125 } 2126 2127 @Override notifyTvMessage(IBinder sessionToken, int type, Bundle data, int userId)2128 public void notifyTvMessage(IBinder sessionToken, int type, Bundle data, int userId) { 2129 final int callingUid = Binder.getCallingUid(); 2130 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2131 userId, "notifyTvmessage"); 2132 final long identity = Binder.clearCallingIdentity(); 2133 try { 2134 synchronized (mLock) { 2135 try { 2136 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2137 .notifyTvMessage(type, data); 2138 } catch (RemoteException | SessionNotFoundException e) { 2139 Slog.e(TAG, "error in notifyTvMessage", e); 2140 } 2141 } 2142 } finally { 2143 Binder.restoreCallingIdentity(identity); 2144 } 2145 } 2146 2147 @Override setTvMessageEnabled(IBinder sessionToken, int type, boolean enabled, int userId)2148 public void setTvMessageEnabled(IBinder sessionToken, int type, boolean enabled, 2149 int userId) { 2150 final int callingUid = Binder.getCallingUid(); 2151 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2152 userId, "setTvMessageEnabled"); 2153 final long identity = Binder.clearCallingIdentity(); 2154 try { 2155 synchronized (mLock) { 2156 try { 2157 final String inputId = 2158 getSessionStateLocked(sessionToken, callingUid, userId).inputId; 2159 mTvInputHardwareManager.setTvMessageEnabled(inputId, type, enabled); 2160 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2161 .setTvMessageEnabled(type, enabled); 2162 } catch (RemoteException | SessionNotFoundException e) { 2163 Slog.e(TAG, "error in setTvMessageEnabled", e); 2164 } 2165 } 2166 } finally { 2167 Binder.restoreCallingIdentity(identity); 2168 } 2169 } 2170 2171 @Override startRecording(IBinder sessionToken, @Nullable Uri programUri, @Nullable Bundle params, int userId)2172 public void startRecording(IBinder sessionToken, @Nullable Uri programUri, 2173 @Nullable Bundle params, int userId) { 2174 final int callingUid = Binder.getCallingUid(); 2175 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2176 userId, "startRecording"); 2177 final long identity = Binder.clearCallingIdentity(); 2178 try { 2179 synchronized (mLock) { 2180 try { 2181 getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording( 2182 programUri, params); 2183 } catch (RemoteException | SessionNotFoundException e) { 2184 Slog.e(TAG, "error in startRecording", e); 2185 } 2186 } 2187 } finally { 2188 Binder.restoreCallingIdentity(identity); 2189 } 2190 } 2191 2192 @Override stopRecording(IBinder sessionToken, int userId)2193 public void stopRecording(IBinder sessionToken, int userId) { 2194 final int callingUid = Binder.getCallingUid(); 2195 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2196 userId, "stopRecording"); 2197 final long identity = Binder.clearCallingIdentity(); 2198 try { 2199 synchronized (mLock) { 2200 try { 2201 getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording(); 2202 } catch (RemoteException | SessionNotFoundException e) { 2203 Slog.e(TAG, "error in stopRecording", e); 2204 } 2205 } 2206 } finally { 2207 Binder.restoreCallingIdentity(identity); 2208 } 2209 } 2210 2211 @Override pauseRecording(IBinder sessionToken, @NonNull Bundle params, int userId)2212 public void pauseRecording(IBinder sessionToken, @NonNull Bundle params, int userId) { 2213 final int callingUid = Binder.getCallingUid(); 2214 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2215 userId, "pauseRecording"); 2216 final long identity = Binder.clearCallingIdentity(); 2217 try { 2218 synchronized (mLock) { 2219 try { 2220 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2221 .pauseRecording(params); 2222 } catch (RemoteException | SessionNotFoundException e) { 2223 Slog.e(TAG, "error in pauseRecording", e); 2224 } 2225 } 2226 } finally { 2227 Binder.restoreCallingIdentity(identity); 2228 } 2229 } 2230 2231 @Override resumeRecording(IBinder sessionToken, @NonNull Bundle params, int userId)2232 public void resumeRecording(IBinder sessionToken, @NonNull Bundle params, int userId) { 2233 final int callingUid = Binder.getCallingUid(); 2234 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2235 userId, "resumeRecording"); 2236 final long identity = Binder.clearCallingIdentity(); 2237 try { 2238 synchronized (mLock) { 2239 try { 2240 getSessionLocked(sessionToken, callingUid, resolvedUserId) 2241 .resumeRecording(params); 2242 } catch (RemoteException | SessionNotFoundException e) { 2243 Slog.e(TAG, "error in resumeRecording", e); 2244 } 2245 } 2246 } finally { 2247 Binder.restoreCallingIdentity(identity); 2248 } 2249 } 2250 2251 @Override getHardwareList()2252 public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { 2253 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 2254 != PackageManager.PERMISSION_GRANTED) { 2255 return null; 2256 } 2257 2258 final long identity = Binder.clearCallingIdentity(); 2259 try { 2260 return mTvInputHardwareManager.getHardwareList(); 2261 } finally { 2262 Binder.restoreCallingIdentity(identity); 2263 } 2264 } 2265 2266 @Override acquireTvInputHardware(int deviceId, ITvInputHardwareCallback callback, TvInputInfo info, int userId, String tvInputSessionId, @TvInputService.PriorityHintUseCaseType int priorityHint)2267 public ITvInputHardware acquireTvInputHardware(int deviceId, 2268 ITvInputHardwareCallback callback, TvInputInfo info, int userId, 2269 String tvInputSessionId, 2270 @TvInputService.PriorityHintUseCaseType int priorityHint) throws RemoteException { 2271 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 2272 != PackageManager.PERMISSION_GRANTED) { 2273 return null; 2274 } 2275 2276 final int callingUid = Binder.getCallingUid(); 2277 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2278 userId, "acquireTvInputHardware"); 2279 final long identity = Binder.clearCallingIdentity(); 2280 try { 2281 return mTvInputHardwareManager.acquireHardware( 2282 deviceId, callback, info, callingUid, resolvedUserId, 2283 tvInputSessionId, priorityHint); 2284 } finally { 2285 Binder.restoreCallingIdentity(identity); 2286 } 2287 } 2288 2289 @Override releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)2290 public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) 2291 throws RemoteException { 2292 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 2293 != PackageManager.PERMISSION_GRANTED) { 2294 return; 2295 } 2296 2297 final int callingUid = Binder.getCallingUid(); 2298 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2299 userId, "releaseTvInputHardware"); 2300 final long identity = Binder.clearCallingIdentity(); 2301 try { 2302 mTvInputHardwareManager.releaseHardware( 2303 deviceId, hardware, callingUid, resolvedUserId); 2304 } finally { 2305 Binder.restoreCallingIdentity(identity); 2306 } 2307 } 2308 2309 @Override getDvbDeviceList()2310 public List<DvbDeviceInfo> getDvbDeviceList() throws RemoteException { 2311 if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE) 2312 != PackageManager.PERMISSION_GRANTED) { 2313 throw new SecurityException("Requires DVB_DEVICE permission"); 2314 } 2315 2316 final long identity = Binder.clearCallingIdentity(); 2317 try { 2318 // Pattern1: /dev/dvb%d.frontend%d 2319 ArrayList<DvbDeviceInfo> deviceInfosFromPattern1 = new ArrayList<>(); 2320 File devDirectory = new File("/dev"); 2321 boolean dvbDirectoryFound = false; 2322 for (String fileName : devDirectory.list()) { 2323 Matcher matcher = sFrontEndDevicePattern.matcher(fileName); 2324 if (matcher.find()) { 2325 int adapterId = Integer.parseInt(matcher.group(1)); 2326 int deviceId = Integer.parseInt(matcher.group(2)); 2327 deviceInfosFromPattern1.add(new DvbDeviceInfo(adapterId, deviceId)); 2328 } 2329 if (TextUtils.equals("dvb", fileName)) { 2330 dvbDirectoryFound = true; 2331 } 2332 } 2333 if (!dvbDirectoryFound) { 2334 return Collections.unmodifiableList(deviceInfosFromPattern1); 2335 } 2336 File dvbDirectory = new File(DVB_DIRECTORY); 2337 // Pattern2: /dev/dvb/adapter%d/frontend%d 2338 ArrayList<DvbDeviceInfo> deviceInfosFromPattern2 = new ArrayList<>(); 2339 for (String fileNameInDvb : dvbDirectory.list()) { 2340 Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb); 2341 if (adapterMatcher.find()) { 2342 int adapterId = Integer.parseInt(adapterMatcher.group(1)); 2343 File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb); 2344 for (String fileNameInAdapter : adapterDirectory.list()) { 2345 Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher( 2346 fileNameInAdapter); 2347 if (frontendMatcher.find()) { 2348 int deviceId = Integer.parseInt(frontendMatcher.group(1)); 2349 deviceInfosFromPattern2.add( 2350 new DvbDeviceInfo(adapterId, deviceId)); 2351 } 2352 } 2353 } 2354 } 2355 return deviceInfosFromPattern2.isEmpty() 2356 ? Collections.unmodifiableList(deviceInfosFromPattern1) 2357 : Collections.unmodifiableList(deviceInfosFromPattern2); 2358 } finally { 2359 Binder.restoreCallingIdentity(identity); 2360 } 2361 } 2362 2363 @Override openDvbDevice(DvbDeviceInfo info, @TvInputManager.DvbDeviceType int deviceType)2364 public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, 2365 @TvInputManager.DvbDeviceType int deviceType) throws RemoteException { 2366 if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE) 2367 != PackageManager.PERMISSION_GRANTED) { 2368 throw new SecurityException("Requires DVB_DEVICE permission"); 2369 } 2370 2371 File devDirectory = new File("/dev"); 2372 boolean dvbDeviceFound = false; 2373 for (String fileName : devDirectory.list()) { 2374 if (TextUtils.equals("dvb", fileName)) { 2375 File dvbDirectory = new File(DVB_DIRECTORY); 2376 for (String fileNameInDvb : dvbDirectory.list()) { 2377 Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb); 2378 if (adapterMatcher.find()) { 2379 File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb); 2380 for (String fileNameInAdapter : adapterDirectory.list()) { 2381 Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher( 2382 fileNameInAdapter); 2383 if (frontendMatcher.find()) { 2384 dvbDeviceFound = true; 2385 break; 2386 } 2387 } 2388 } 2389 if (dvbDeviceFound) { 2390 break; 2391 } 2392 } 2393 } 2394 if (dvbDeviceFound) { 2395 break; 2396 } 2397 } 2398 2399 final long identity = Binder.clearCallingIdentity(); 2400 try { 2401 String deviceFileName; 2402 switch (deviceType) { 2403 case TvInputManager.DVB_DEVICE_DEMUX: 2404 deviceFileName = String.format(dvbDeviceFound 2405 ? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d", 2406 info.getAdapterId(), info.getDeviceId()); 2407 break; 2408 case TvInputManager.DVB_DEVICE_DVR: 2409 deviceFileName = String.format(dvbDeviceFound 2410 ? "/dev/dvb/adapter%d/dvr%d" : "/dev/dvb%d.dvr%d", 2411 info.getAdapterId(), info.getDeviceId()); 2412 break; 2413 case TvInputManager.DVB_DEVICE_FRONTEND: 2414 deviceFileName = String.format(dvbDeviceFound 2415 ? "/dev/dvb/adapter%d/frontend%d" : "/dev/dvb%d.frontend%d", 2416 info.getAdapterId(), info.getDeviceId()); 2417 break; 2418 default: 2419 throw new IllegalArgumentException("Invalid DVB device: " + deviceType); 2420 } 2421 try { 2422 // The DVB frontend device only needs to be opened in read/write mode, which 2423 // allows performing tuning operations. The DVB demux and DVR device are enough 2424 // to be opened in read only mode. 2425 return ParcelFileDescriptor.open(new File(deviceFileName), 2426 TvInputManager.DVB_DEVICE_FRONTEND == deviceType 2427 ? ParcelFileDescriptor.MODE_READ_WRITE 2428 : ParcelFileDescriptor.MODE_READ_ONLY); 2429 } catch (FileNotFoundException e) { 2430 return null; 2431 } 2432 } finally { 2433 Binder.restoreCallingIdentity(identity); 2434 } 2435 } 2436 2437 @Override getAvailableTvStreamConfigList(String inputId, int userId)2438 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId) 2439 throws RemoteException { 2440 ensureCaptureTvInputPermission(); 2441 2442 final int callingUid = Binder.getCallingUid(); 2443 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2444 userId, "getAvailableTvStreamConfigList"); 2445 final long identity = Binder.clearCallingIdentity(); 2446 try { 2447 return mTvInputHardwareManager.getAvailableTvStreamConfigList( 2448 inputId, callingUid, resolvedUserId); 2449 } finally { 2450 Binder.restoreCallingIdentity(identity); 2451 } 2452 } 2453 2454 @Override captureFrame(String inputId, Surface surface, TvStreamConfig config, int userId)2455 public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config, 2456 int userId) 2457 throws RemoteException { 2458 ensureCaptureTvInputPermission(); 2459 2460 final int callingUid = Binder.getCallingUid(); 2461 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2462 userId, "captureFrame"); 2463 final long identity = Binder.clearCallingIdentity(); 2464 try { 2465 String hardwareInputId = null; 2466 synchronized (mLock) { 2467 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 2468 if (userState.inputMap.get(inputId) == null) { 2469 Slog.e(TAG, "input not found for " + inputId); 2470 return false; 2471 } 2472 for (SessionState sessionState : userState.sessionStateMap.values()) { 2473 if (sessionState.inputId.equals(inputId) 2474 && sessionState.hardwareSessionToken != null) { 2475 hardwareInputId = userState.sessionStateMap.get( 2476 sessionState.hardwareSessionToken).inputId; 2477 break; 2478 } 2479 } 2480 } 2481 return mTvInputHardwareManager.captureFrame( 2482 (hardwareInputId != null) ? hardwareInputId : inputId, 2483 surface, config, callingUid, resolvedUserId); 2484 } finally { 2485 Binder.restoreCallingIdentity(identity); 2486 } 2487 } 2488 2489 @Override isSingleSessionActive(int userId)2490 public boolean isSingleSessionActive(int userId) throws RemoteException { 2491 ensureCaptureTvInputPermission(); 2492 final int callingUid = Binder.getCallingUid(); 2493 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2494 userId, "isSingleSessionActive"); 2495 final long identity = Binder.clearCallingIdentity(); 2496 try { 2497 synchronized (mLock) { 2498 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 2499 if (userState.sessionStateMap.size() == 1) { 2500 return true; 2501 } else if (userState.sessionStateMap.size() == 2) { 2502 SessionState[] sessionStates = userState.sessionStateMap.values().toArray( 2503 new SessionState[2]); 2504 // Check if there is a wrapper input. 2505 return sessionStates[0].hardwareSessionToken != null 2506 || sessionStates[1].hardwareSessionToken != null; 2507 } 2508 return false; 2509 } 2510 } finally { 2511 Binder.restoreCallingIdentity(identity); 2512 } 2513 } 2514 ensureCaptureTvInputPermission()2515 private void ensureCaptureTvInputPermission() { 2516 if (mContext.checkCallingPermission( 2517 android.Manifest.permission.CAPTURE_TV_INPUT) 2518 != PackageManager.PERMISSION_GRANTED) { 2519 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 2520 } 2521 } 2522 2523 @Override requestChannelBrowsable(Uri channelUri, int userId)2524 public void requestChannelBrowsable(Uri channelUri, int userId) 2525 throws RemoteException { 2526 final String callingPackageName = getCallingPackageName(); 2527 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 2528 Binder.getCallingUid(), userId, "requestChannelBrowsable"); 2529 final long identity = Binder.clearCallingIdentity(); 2530 try { 2531 Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED); 2532 List<ResolveInfo> list = getContext().getPackageManager() 2533 .queryBroadcastReceivers(intent, 0); 2534 if (list != null) { 2535 for (ResolveInfo info : list) { 2536 String receiverPackageName = info.activityInfo.packageName; 2537 intent.putExtra(TvContract.EXTRA_CHANNEL_ID, ContentUris.parseId( 2538 channelUri)); 2539 intent.putExtra(TvContract.EXTRA_PACKAGE_NAME, callingPackageName); 2540 intent.setPackage(receiverPackageName); 2541 getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId)); 2542 } 2543 } 2544 } finally { 2545 Binder.restoreCallingIdentity(identity); 2546 } 2547 } 2548 2549 @Override requestBroadcastInfo(IBinder sessionToken, BroadcastInfoRequest request, int userId)2550 public void requestBroadcastInfo(IBinder sessionToken, BroadcastInfoRequest request, 2551 int userId) { 2552 final int callingUid = Binder.getCallingUid(); 2553 final int callingPid = Binder.getCallingPid(); 2554 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, 2555 userId, "requestBroadcastInfo"); 2556 final long identity = Binder.clearCallingIdentity(); 2557 try { 2558 synchronized (mLock) { 2559 try { 2560 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 2561 resolvedUserId); 2562 getSessionLocked(sessionState).requestBroadcastInfo(request); 2563 } catch (RemoteException | SessionNotFoundException e) { 2564 Slog.e(TAG, "error in requestBroadcastInfo", e); 2565 } 2566 } 2567 } finally { 2568 Binder.restoreCallingIdentity(identity); 2569 } 2570 } 2571 2572 @Override removeBroadcastInfo(IBinder sessionToken, int requestId, int userId)2573 public void removeBroadcastInfo(IBinder sessionToken, int requestId, int userId) { 2574 final int callingUid = Binder.getCallingUid(); 2575 final int callingPid = Binder.getCallingPid(); 2576 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, 2577 userId, "removeBroadcastInfo"); 2578 final long identity = Binder.clearCallingIdentity(); 2579 try { 2580 synchronized (mLock) { 2581 try { 2582 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 2583 resolvedUserId); 2584 getSessionLocked(sessionState).removeBroadcastInfo(requestId); 2585 } catch (RemoteException | SessionNotFoundException e) { 2586 Slog.e(TAG, "error in removeBroadcastInfo", e); 2587 } 2588 } 2589 } finally { 2590 Binder.restoreCallingIdentity(identity); 2591 } 2592 } 2593 2594 @Override requestAd(IBinder sessionToken, AdRequest request, int userId)2595 public void requestAd(IBinder sessionToken, AdRequest request, int userId) { 2596 final int callingUid = Binder.getCallingUid(); 2597 final int callingPid = Binder.getCallingPid(); 2598 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, 2599 userId, "requestAd"); 2600 final long identity = Binder.clearCallingIdentity(); 2601 try { 2602 synchronized (mLock) { 2603 try { 2604 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 2605 resolvedUserId); 2606 getSessionLocked(sessionState).requestAd(request); 2607 } catch (RemoteException | SessionNotFoundException e) { 2608 Slog.e(TAG, "error in requestAd", e); 2609 } 2610 } 2611 } finally { 2612 Binder.restoreCallingIdentity(identity); 2613 } 2614 } 2615 2616 @Override notifyAdBufferReady( IBinder sessionToken, AdBuffer buffer, int userId)2617 public void notifyAdBufferReady( 2618 IBinder sessionToken, AdBuffer buffer, int userId) { 2619 final int callingUid = Binder.getCallingUid(); 2620 final int callingPid = Binder.getCallingPid(); 2621 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, 2622 userId, "notifyAdBuffer"); 2623 final long identity = Binder.clearCallingIdentity(); 2624 try { 2625 synchronized (mLock) { 2626 try { 2627 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 2628 resolvedUserId); 2629 getSessionLocked(sessionState).notifyAdBufferReady(buffer); 2630 } catch (RemoteException | SessionNotFoundException e) { 2631 Slog.e(TAG, "error in notifyAdBuffer", e); 2632 } finally { 2633 if (buffer != null) { 2634 buffer.getSharedMemory().close(); 2635 } 2636 } 2637 } 2638 } finally { 2639 Binder.restoreCallingIdentity(identity); 2640 } 2641 } 2642 2643 @Override getClientPid(String sessionId)2644 public int getClientPid(String sessionId) { 2645 ensureTunerResourceAccessPermission(); 2646 final long identity = Binder.clearCallingIdentity(); 2647 2648 int clientPid = TvInputManager.UNKNOWN_CLIENT_PID; 2649 try { 2650 synchronized (mLock) { 2651 try { 2652 clientPid = getClientPidLocked(sessionId); 2653 } catch (ClientPidNotFoundException e) { 2654 Slog.e(TAG, "error in getClientPid", e); 2655 } 2656 } 2657 } finally { 2658 Binder.restoreCallingIdentity(identity); 2659 } 2660 return clientPid; 2661 } 2662 2663 @Override getClientPriority(int useCase, String sessionId)2664 public int getClientPriority(int useCase, String sessionId) { 2665 ensureTunerResourceAccessPermission(); 2666 final int callingPid = Binder.getCallingPid(); 2667 final long identity = Binder.clearCallingIdentity(); 2668 try { 2669 int clientPid = TvInputManager.UNKNOWN_CLIENT_PID; 2670 if (sessionId != null) { 2671 synchronized (mLock) { 2672 try { 2673 clientPid = getClientPidLocked(sessionId); 2674 } catch (ClientPidNotFoundException e) { 2675 Slog.e(TAG, "error in getClientPriority", e); 2676 } 2677 } 2678 } else { 2679 clientPid = callingPid; 2680 } 2681 TunerResourceManager trm = (TunerResourceManager) 2682 mContext.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE); 2683 return trm.getClientPriority(useCase, clientPid); 2684 } finally { 2685 Binder.restoreCallingIdentity(identity); 2686 } 2687 } 2688 2689 @Override getCurrentTunedInfos(@serIdInt int userId)2690 public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) { 2691 if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO) 2692 != PackageManager.PERMISSION_GRANTED) { 2693 throw new SecurityException( 2694 "The caller does not have access tuned info permission"); 2695 } 2696 int callingPid = Binder.getCallingPid(); 2697 int callingUid = Binder.getCallingUid(); 2698 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, 2699 "getTvCurrentChannelInfos"); 2700 synchronized (mLock) { 2701 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 2702 return getCurrentTunedInfosInternalLocked(userState, callingPid, callingUid); 2703 } 2704 } 2705 2706 /** 2707 * Add a hardware device in the TvInputHardwareManager for CTS testing 2708 * purpose. 2709 * 2710 * @param deviceId the id of the adding hardware device. 2711 */ 2712 @Override addHardwareDevice(int deviceId)2713 public void addHardwareDevice(int deviceId) { 2714 TvInputHardwareInfo info = new TvInputHardwareInfo.Builder() 2715 .deviceId(deviceId) 2716 .type(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) 2717 .audioType(DEVICE_NONE) 2718 .audioAddress("0") 2719 .hdmiPortId(0) 2720 .build(); 2721 TvStreamConfig[] configs = { 2722 new TvStreamConfig.Builder().streamId(19001) 2723 .generation(1).maxHeight(600).maxWidth(800).type(1).build()}; 2724 mTvInputHardwareManager.onDeviceAvailable(info, configs); 2725 } 2726 2727 /** 2728 * Remove a hardware device in the TvInputHardwareManager for CTS testing 2729 * purpose. 2730 * 2731 * @param deviceId the id of the removing hardware device. 2732 */ 2733 @Override removeHardwareDevice(int deviceId)2734 public void removeHardwareDevice(int deviceId) { 2735 mTvInputHardwareManager.onDeviceUnavailable(deviceId); 2736 } 2737 2738 @GuardedBy("mLock") getClientPidLocked(String sessionId)2739 private int getClientPidLocked(String sessionId) 2740 throws ClientPidNotFoundException { 2741 if (mSessionIdToSessionStateMap.get(sessionId) == null) { 2742 throw new ClientPidNotFoundException("Client Pid not found with sessionId " 2743 + sessionId); 2744 } 2745 return mSessionIdToSessionStateMap.get(sessionId).callingPid; 2746 } 2747 ensureTunerResourceAccessPermission()2748 private void ensureTunerResourceAccessPermission() { 2749 if (mContext.checkCallingPermission( 2750 android.Manifest.permission.TUNER_RESOURCE_ACCESS) 2751 != PackageManager.PERMISSION_GRANTED) { 2752 throw new SecurityException("Requires TUNER_RESOURCE_ACCESS permission"); 2753 } 2754 } 2755 ensureTisExtensionInterfacePermission()2756 private void ensureTisExtensionInterfacePermission() { 2757 if (mContext.checkCallingPermission( 2758 android.Manifest.permission.TIS_EXTENSION_INTERFACE) 2759 != PackageManager.PERMISSION_GRANTED) { 2760 throw new SecurityException("Requires TIS_EXTENSION_INTERFACE permission"); 2761 } 2762 } 2763 2764 @NeverCompile // Avoid size overhead of debugging code. 2765 @Override 2766 @SuppressWarnings("resource") dump(FileDescriptor fd, final PrintWriter writer, String[] args)2767 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 2768 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 2769 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 2770 2771 synchronized (mLock) { 2772 pw.println("User Ids (Current user: " + mCurrentUserId + "):"); 2773 pw.increaseIndent(); 2774 for (int i = 0; i < mUserStates.size(); i++) { 2775 int userId = mUserStates.keyAt(i); 2776 pw.println(Integer.valueOf(userId)); 2777 } 2778 pw.decreaseIndent(); 2779 2780 for (int i = 0; i < mUserStates.size(); i++) { 2781 int userId = mUserStates.keyAt(i); 2782 UserState userState = getOrCreateUserStateLocked(userId); 2783 pw.println("UserState (" + userId + "):"); 2784 pw.increaseIndent(); 2785 2786 pw.println("inputMap: inputId -> TvInputState"); 2787 pw.increaseIndent(); 2788 for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) { 2789 pw.println(entry.getKey() + ": " + entry.getValue()); 2790 } 2791 pw.decreaseIndent(); 2792 2793 pw.println("packageSet:"); 2794 pw.increaseIndent(); 2795 for (String packageName : userState.packageSet) { 2796 pw.println(packageName); 2797 } 2798 pw.decreaseIndent(); 2799 2800 pw.println("clientStateMap: ITvInputClient -> ClientState"); 2801 pw.increaseIndent(); 2802 for (Map.Entry<IBinder, ClientState> entry : 2803 userState.clientStateMap.entrySet()) { 2804 ClientState client = entry.getValue(); 2805 pw.println(entry.getKey() + ": " + client); 2806 2807 pw.increaseIndent(); 2808 2809 pw.println("sessionTokens:"); 2810 pw.increaseIndent(); 2811 for (IBinder token : client.sessionTokens) { 2812 pw.println("" + token); 2813 } 2814 pw.decreaseIndent(); 2815 2816 pw.println("clientTokens: " + client.clientToken); 2817 pw.println("userId: " + client.userId); 2818 2819 pw.decreaseIndent(); 2820 } 2821 pw.decreaseIndent(); 2822 2823 pw.println("serviceStateMap: ComponentName -> ServiceState"); 2824 pw.increaseIndent(); 2825 for (Map.Entry<ComponentName, ServiceState> entry : 2826 userState.serviceStateMap.entrySet()) { 2827 ServiceState service = entry.getValue(); 2828 pw.println(entry.getKey() + ": " + service); 2829 2830 pw.increaseIndent(); 2831 2832 pw.println("sessionTokens:"); 2833 pw.increaseIndent(); 2834 for (IBinder token : service.sessionTokens) { 2835 pw.println("" + token); 2836 } 2837 pw.decreaseIndent(); 2838 2839 pw.println("service: " + service.service); 2840 pw.println("callback: " + service.callback); 2841 pw.println("bound: " + service.bound); 2842 pw.println("reconnecting: " + service.reconnecting); 2843 2844 pw.decreaseIndent(); 2845 } 2846 pw.decreaseIndent(); 2847 2848 pw.println("sessionStateMap: ITvInputSession -> SessionState"); 2849 pw.increaseIndent(); 2850 for (Map.Entry<IBinder, SessionState> entry : 2851 userState.sessionStateMap.entrySet()) { 2852 SessionState session = entry.getValue(); 2853 pw.println(entry.getKey() + ": " + session); 2854 2855 pw.increaseIndent(); 2856 pw.println("inputId: " + session.inputId); 2857 pw.println("sessionId: " + session.sessionId); 2858 pw.println("client: " + session.client); 2859 pw.println("seq: " + session.seq); 2860 pw.println("callingUid: " + session.callingUid); 2861 pw.println("callingPid: " + session.callingPid); 2862 pw.println("userId: " + session.userId); 2863 pw.println("sessionToken: " + session.sessionToken); 2864 pw.println("session: " + session.session); 2865 pw.println("hardwareSessionToken: " + session.hardwareSessionToken); 2866 pw.decreaseIndent(); 2867 } 2868 pw.decreaseIndent(); 2869 2870 pw.println("mCallbacks:"); 2871 pw.increaseIndent(); 2872 int n = userState.mCallbacks.beginBroadcast(); 2873 for (int j = 0; j < n; ++j) { 2874 pw.println(userState.mCallbacks.getRegisteredCallbackItem(j)); 2875 } 2876 userState.mCallbacks.finishBroadcast(); 2877 pw.decreaseIndent(); 2878 2879 pw.println("mainSessionToken: " + userState.mainSessionToken); 2880 pw.decreaseIndent(); 2881 } 2882 } 2883 mTvInputHardwareManager.dump(fd, writer, args); 2884 } 2885 } 2886 2887 @Nullable getTvInputState( SessionState sessionState, @Nullable UserState userState)2888 private static TvInputState getTvInputState( 2889 SessionState sessionState, 2890 @Nullable UserState userState) { 2891 if (userState != null) { 2892 return userState.inputMap.get(sessionState.inputId); 2893 } 2894 return null; 2895 } 2896 2897 @GuardedBy("mLock") getCurrentTunedInfosInternalLocked( UserState userState, int callingPid, int callingUid)2898 private List<TunedInfo> getCurrentTunedInfosInternalLocked( 2899 UserState userState, int callingPid, int callingUid) { 2900 List<TunedInfo> channelInfos = new ArrayList<>(); 2901 boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid); 2902 for (SessionState state : userState.sessionStateMap.values()) { 2903 if (state.isCurrent) { 2904 Integer appTag; 2905 int appType; 2906 if (state.callingUid == callingUid) { 2907 appTag = APP_TAG_SELF; 2908 appType = TunedInfo.APP_TYPE_SELF; 2909 } else { 2910 appTag = userState.mAppTagMap.get(state.callingUid); 2911 if (appTag == null) { 2912 appTag = userState.mNextAppTag++; 2913 userState.mAppTagMap.put(state.callingUid, appTag); 2914 } 2915 appType = isSystemApp(state.componentName.getPackageName()) 2916 ? TunedInfo.APP_TYPE_SYSTEM 2917 : TunedInfo.APP_TYPE_NON_SYSTEM; 2918 } 2919 channelInfos.add(new TunedInfo( 2920 state.inputId, 2921 watchedProgramsAccess ? state.currentChannel : null, 2922 state.isRecordingSession, 2923 state.isVisible, 2924 state.isMainSession, 2925 appType, 2926 appTag)); 2927 } 2928 } 2929 return channelInfos; 2930 } 2931 hasAccessWatchedProgramsPermission(int callingPid, int callingUid)2932 private boolean hasAccessWatchedProgramsPermission(int callingPid, int callingUid) { 2933 return mContext.checkPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS, callingPid, callingUid) 2934 == PackageManager.PERMISSION_GRANTED; 2935 } 2936 isSystemApp(String pkg)2937 private boolean isSystemApp(String pkg) { 2938 try { 2939 return (mContext.getPackageManager().getApplicationInfo(pkg, 0).flags 2940 & ApplicationInfo.FLAG_SYSTEM) != 0; 2941 } catch (NameNotFoundException e) { 2942 return false; 2943 } 2944 } 2945 2946 /** 2947 * Log Tune state changes to {@link FrameworkStatsLog}. 2948 * 2949 * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. 2950 * Inspect the code at: 2951 * 2952 * <ul> 2953 * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState 2954 * <li>{@link #logTuneStateChanged} 2955 * <li>{@link TvInputManagerService.BinderService#createSession} 2956 * <li>{@link SessionState#sessionId} 2957 * </ul> 2958 */ logTuneStateChanged(int state, SessionState sessionState, @Nullable TvInputState inputState)2959 private void logTuneStateChanged(int state, SessionState sessionState, 2960 @Nullable TvInputState inputState) { 2961 int tisUid = Process.INVALID_UID; 2962 int inputType = FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__TYPE__TIF_INPUT_TYPE_UNKNOWN; 2963 int inputId = 0; 2964 int hdmiPort = 0; 2965 if (inputState != null) { 2966 tisUid = inputState.uid; 2967 inputType = inputState.info.getType(); 2968 if (inputType == TvInputInfo.TYPE_TUNER) { 2969 inputType = FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__TYPE__TUNER; 2970 } 2971 inputId = inputState.inputNumber; 2972 HdmiDeviceInfo hdmiDeviceInfo = inputState.info.getHdmiDeviceInfo(); 2973 if (hdmiDeviceInfo != null) { 2974 hdmiPort = hdmiDeviceInfo.getPortId(); 2975 } 2976 } 2977 FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED, 2978 new int[]{sessionState.callingUid, 2979 tisUid}, 2980 new String[]{"tif_player", "tv_input_service"}, 2981 state, 2982 sessionState.sessionId, 2983 inputType, 2984 inputId, 2985 hdmiPort); 2986 } 2987 2988 private static final class UserState { 2989 // A mapping from the TV input id to its TvInputState. 2990 private Map<String, TvInputState> inputMap = new HashMap<>(); 2991 2992 // A set of all TV input packages. 2993 private final Set<String> packageSet = new HashSet<>(); 2994 2995 // A list of all TV content rating systems defined. 2996 private final List<TvContentRatingSystemInfo> 2997 contentRatingSystemList = new ArrayList<>(); 2998 2999 // A mapping from the token of a client to its state. 3000 private final Map<IBinder, ClientState> clientStateMap = new HashMap<>(); 3001 3002 // A mapping from the name of a TV input service to its state. 3003 private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>(); 3004 3005 // A mapping from the token of a TV input session to its state. 3006 private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>(); 3007 3008 // A list of callbacks. 3009 private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks = 3010 new RemoteCallbackList<>(); 3011 3012 private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap = 3013 new HashMap<>(); 3014 3015 // The token of a "main" TV input session. 3016 private IBinder mainSessionToken = null; 3017 3018 // Persistent data store for all internal settings maintained by the TV input manager 3019 // service. 3020 private final PersistentDataStore persistentDataStore; 3021 3022 @GuardedBy("TvInputManagerService.this.mLock") 3023 private final Map<Integer, Integer> mAppTagMap = new HashMap<>(); 3024 @GuardedBy("TvInputManagerService.this.mLock") 3025 private int mNextAppTag = 1; 3026 UserState(Context context, int userId)3027 private UserState(Context context, int userId) { 3028 persistentDataStore = new PersistentDataStore(context, userId); 3029 } 3030 } 3031 3032 private final class ClientState implements IBinder.DeathRecipient { 3033 private final List<IBinder> sessionTokens = new ArrayList<>(); 3034 3035 private IBinder clientToken; 3036 private final int userId; 3037 ClientState(IBinder clientToken, int userId)3038 ClientState(IBinder clientToken, int userId) { 3039 this.clientToken = clientToken; 3040 this.userId = userId; 3041 } 3042 isEmpty()3043 public boolean isEmpty() { 3044 return sessionTokens.isEmpty(); 3045 } 3046 3047 @Override binderDied()3048 public void binderDied() { 3049 synchronized (mLock) { 3050 UserState userState = getOrCreateUserStateLocked(userId); 3051 // DO NOT remove the client state of clientStateMap in this method. It will be 3052 // removed in releaseSessionLocked(). 3053 ClientState clientState = userState.clientStateMap.get(clientToken); 3054 if (clientState != null) { 3055 while (clientState.sessionTokens.size() > 0) { 3056 IBinder sessionToken = clientState.sessionTokens.get(0); 3057 releaseSessionLocked( 3058 sessionToken, Process.SYSTEM_UID, userId); 3059 // the releaseSessionLocked function may return before the sessionToken 3060 // is removed if the related sessionState is null. So need to check again 3061 // to avoid death curculation. 3062 if (clientState.sessionTokens.contains(sessionToken)) { 3063 Slog.d(TAG, "remove sessionToken " + sessionToken + " for " + clientToken); 3064 clientState.sessionTokens.remove(sessionToken); 3065 } 3066 } 3067 } 3068 clientToken = null; 3069 } 3070 } 3071 } 3072 3073 private final class ServiceState { 3074 private final List<IBinder> sessionTokens = new ArrayList<>(); 3075 private final ServiceConnection connection; 3076 private final ComponentName component; 3077 private final boolean isHardware; 3078 private final Map<String, TvInputInfo> hardwareInputMap = new HashMap<>(); 3079 3080 private ITvInputService service; 3081 private ServiceCallback callback; 3082 private boolean bound; 3083 private boolean reconnecting; 3084 ServiceState(ComponentName component, int userId)3085 private ServiceState(ComponentName component, int userId) { 3086 this.component = component; 3087 this.connection = new InputServiceConnection(component, userId); 3088 this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component); 3089 } 3090 } 3091 3092 private static final class TvInputState { 3093 3094 /** A TvInputInfo object which represents the TV input. */ 3095 private TvInputInfo info; 3096 3097 /** 3098 * ID unique to a specific TvInputService. 3099 */ 3100 private int inputNumber; 3101 3102 /** 3103 * The kernel user-ID that has been assigned to the application the TvInput is a part of. 3104 * 3105 * <p> 3106 * Currently this is not a unique ID (multiple applications can have 3107 * the same uid). 3108 */ 3109 private int uid; 3110 3111 /** 3112 * The state of TV input. 3113 * 3114 * <p> 3115 * Connected by default 3116 */ 3117 private int state = INPUT_STATE_CONNECTED; 3118 3119 @Override toString()3120 public String toString() { 3121 return "info: " + info + "; state: " + state; 3122 } 3123 } 3124 3125 private final class SessionState implements IBinder.DeathRecipient { 3126 private final String inputId; 3127 3128 /** 3129 * A randomly generated id for this this session. 3130 * 3131 * <p>This field contains no user or device reference and is large enough to be 3132 * effectively globally unique. 3133 * 3134 * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. 3135 * Inspect the code at: 3136 * 3137 * <ul> 3138 * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState 3139 * <li>{@link #logTuneStateChanged} 3140 * <li>{@link TvInputManagerService.BinderService#createSession} 3141 * <li>{@link SessionState#sessionId} 3142 * </ul> 3143 */ 3144 private final String sessionId; 3145 private final ComponentName componentName; 3146 private final boolean isRecordingSession; 3147 private final ITvInputClient client; 3148 private final AttributionSource tvAppAttributionSource; 3149 private final int seq; 3150 /** 3151 * The {code UID} of the application that created the session. 3152 * 3153 * <p> 3154 * The application is usually the TIF Player. 3155 */ 3156 private final int callingUid; 3157 /** 3158 * The {@code PID} of the application that created the session. 3159 * 3160 * <p> 3161 * The application is usually the TIF Player. 3162 */ 3163 private final int callingPid; 3164 private final int userId; 3165 private final IBinder sessionToken; 3166 private ITvInputSession session; 3167 // Not null if this session represents an external device connected to a hardware TV input. 3168 private IBinder hardwareSessionToken; 3169 3170 private boolean isCurrent = false; 3171 private Uri currentChannel = null; 3172 private boolean isVisible = false; 3173 private boolean isMainSession = false; 3174 SessionState(IBinder sessionToken, String inputId, ComponentName componentName, boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, int callingPid, int userId, String sessionId, AttributionSource tvAppAttributionSource)3175 private SessionState(IBinder sessionToken, String inputId, ComponentName componentName, 3176 boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, 3177 int callingPid, int userId, String sessionId, 3178 AttributionSource tvAppAttributionSource) { 3179 this.sessionToken = sessionToken; 3180 this.inputId = inputId; 3181 this.componentName = componentName; 3182 this.isRecordingSession = isRecordingSession; 3183 this.client = client; 3184 this.seq = seq; 3185 this.callingUid = callingUid; 3186 this.callingPid = callingPid; 3187 this.userId = userId; 3188 this.sessionId = sessionId; 3189 this.tvAppAttributionSource = tvAppAttributionSource; 3190 } 3191 3192 @Override binderDied()3193 public void binderDied() { 3194 synchronized (mLock) { 3195 session = null; 3196 clearSessionAndNotifyClientLocked(this); 3197 } 3198 } 3199 } 3200 3201 private final class InputServiceConnection implements ServiceConnection { 3202 private final ComponentName mComponent; 3203 private final int mUserId; 3204 InputServiceConnection(ComponentName component, int userId)3205 private InputServiceConnection(ComponentName component, int userId) { 3206 mComponent = component; 3207 mUserId = userId; 3208 } 3209 3210 @Override onServiceConnected(ComponentName component, IBinder service)3211 public void onServiceConnected(ComponentName component, IBinder service) { 3212 if (DEBUG) { 3213 Slog.d(TAG, "onServiceConnected(component=" + component + ")"); 3214 } 3215 synchronized (mLock) { 3216 UserState userState = getUserStateLocked(mUserId); 3217 if (userState == null) { 3218 // The user was removed while connecting. 3219 mContext.unbindService(this); 3220 return; 3221 } 3222 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 3223 serviceState.service = ITvInputService.Stub.asInterface(service); 3224 3225 // Register a callback, if we need to. 3226 if (serviceState.isHardware && serviceState.callback == null) { 3227 serviceState.callback = new ServiceCallback(mComponent, mUserId); 3228 try { 3229 serviceState.service.registerCallback(serviceState.callback); 3230 } catch (RemoteException e) { 3231 Slog.e(TAG, "error in registerCallback", e); 3232 } 3233 } 3234 3235 List<IBinder> tokensToBeRemoved = new ArrayList<>(); 3236 3237 // And create sessions, if any. 3238 for (IBinder sessionToken : serviceState.sessionTokens) { 3239 if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) { 3240 tokensToBeRemoved.add(sessionToken); 3241 } 3242 } 3243 3244 for (IBinder sessionToken : tokensToBeRemoved) { 3245 removeSessionStateLocked(sessionToken, mUserId); 3246 } 3247 3248 for (TvInputState inputState : userState.inputMap.values()) { 3249 if (inputState.info.getComponent().equals(component) 3250 && inputState.state != INPUT_STATE_CONNECTED) { 3251 notifyInputStateChangedLocked(userState, inputState.info.getId(), 3252 inputState.state, null); 3253 } 3254 } 3255 3256 if (serviceState.isHardware) { 3257 serviceState.hardwareInputMap.clear(); 3258 for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) { 3259 try { 3260 serviceState.service.notifyHardwareAdded(hardware); 3261 } catch (RemoteException e) { 3262 Slog.e(TAG, "error in notifyHardwareAdded", e); 3263 } 3264 } 3265 for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) { 3266 try { 3267 serviceState.service.notifyHdmiDeviceAdded(device); 3268 } catch (RemoteException e) { 3269 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 3270 } 3271 } 3272 } 3273 } 3274 } 3275 3276 @Override onServiceDisconnected(ComponentName component)3277 public void onServiceDisconnected(ComponentName component) { 3278 if (DEBUG) { 3279 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")"); 3280 } 3281 if (!mComponent.equals(component)) { 3282 throw new IllegalArgumentException("Mismatched ComponentName: " 3283 + mComponent + " (expected), " + component + " (actual)."); 3284 } 3285 synchronized (mLock) { 3286 UserState userState = getOrCreateUserStateLocked(mUserId); 3287 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 3288 if (serviceState != null) { 3289 serviceState.reconnecting = true; 3290 serviceState.bound = false; 3291 serviceState.service = null; 3292 serviceState.callback = null; 3293 3294 abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId); 3295 } 3296 } 3297 } 3298 } 3299 3300 private final class ServiceCallback extends ITvInputServiceCallback.Stub { 3301 private final ComponentName mComponent; 3302 private final int mUserId; 3303 ServiceCallback(ComponentName component, int userId)3304 ServiceCallback(ComponentName component, int userId) { 3305 mComponent = component; 3306 mUserId = userId; 3307 } 3308 ensureHardwarePermission()3309 private void ensureHardwarePermission() { 3310 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 3311 != PackageManager.PERMISSION_GRANTED) { 3312 throw new SecurityException("The caller does not have hardware permission"); 3313 } 3314 } 3315 ensureValidInput(TvInputInfo inputInfo)3316 private void ensureValidInput(TvInputInfo inputInfo) { 3317 if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) { 3318 throw new IllegalArgumentException("Invalid TvInputInfo"); 3319 } 3320 } 3321 3322 @GuardedBy("mLock") addHardwareInputLocked(TvInputInfo inputInfo)3323 private void addHardwareInputLocked(TvInputInfo inputInfo) { 3324 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 3325 serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo); 3326 buildTvInputListLocked(mUserId, null); 3327 } 3328 addHardwareInput(int deviceId, TvInputInfo inputInfo)3329 public void addHardwareInput(int deviceId, TvInputInfo inputInfo) { 3330 ensureHardwarePermission(); 3331 ensureValidInput(inputInfo); 3332 final long identity = Binder.clearCallingIdentity(); 3333 try { 3334 synchronized (mLock) { 3335 mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo); 3336 addHardwareInputLocked(inputInfo); 3337 } 3338 } finally { 3339 Binder.restoreCallingIdentity(identity); 3340 } 3341 } 3342 addHdmiInput(int id, TvInputInfo inputInfo)3343 public void addHdmiInput(int id, TvInputInfo inputInfo) { 3344 ensureHardwarePermission(); 3345 ensureValidInput(inputInfo); 3346 final long identity = Binder.clearCallingIdentity(); 3347 try { 3348 synchronized (mLock) { 3349 mTvInputHardwareManager.addHdmiInput(id, inputInfo); 3350 addHardwareInputLocked(inputInfo); 3351 } 3352 } finally { 3353 Binder.restoreCallingIdentity(identity); 3354 } 3355 } 3356 removeHardwareInput(String inputId)3357 public void removeHardwareInput(String inputId) { 3358 ensureHardwarePermission(); 3359 final long identity = Binder.clearCallingIdentity(); 3360 try { 3361 synchronized (mLock) { 3362 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 3363 boolean removed = serviceState.hardwareInputMap.remove(inputId) != null; 3364 if (removed) { 3365 buildTvInputListLocked(mUserId, null); 3366 mTvInputHardwareManager.removeHardwareInput(inputId); 3367 } else { 3368 Slog.e(TAG, "failed to remove input " + inputId); 3369 } 3370 } 3371 } finally { 3372 Binder.restoreCallingIdentity(identity); 3373 } 3374 } 3375 } 3376 3377 private final class SessionCallback extends ITvInputSessionCallback.Stub { 3378 private final SessionState mSessionState; 3379 private final InputChannel[] mChannels; 3380 SessionCallback(SessionState sessionState, InputChannel[] channels)3381 SessionCallback(SessionState sessionState, InputChannel[] channels) { 3382 mSessionState = sessionState; 3383 mChannels = channels; 3384 } 3385 3386 @Override onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken)3387 public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) { 3388 if (DEBUG) { 3389 Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")"); 3390 } 3391 synchronized (mLock) { 3392 mSessionState.session = session; 3393 mSessionState.hardwareSessionToken = hardwareSessionToken; 3394 if (session != null && addSessionTokenToClientStateLocked(session)) { 3395 sendSessionTokenToClientLocked(mSessionState.client, 3396 mSessionState.inputId, mSessionState.sessionToken, mChannels[0], 3397 mSessionState.seq); 3398 } else { 3399 removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId); 3400 sendSessionTokenToClientLocked(mSessionState.client, 3401 mSessionState.inputId, null, null, mSessionState.seq); 3402 } 3403 mChannels[0].dispose(); 3404 } 3405 } 3406 3407 @GuardedBy("mLock") addSessionTokenToClientStateLocked(ITvInputSession session)3408 private boolean addSessionTokenToClientStateLocked(ITvInputSession session) { 3409 try { 3410 session.asBinder().linkToDeath(mSessionState, 0); 3411 } catch (RemoteException e) { 3412 Slog.e(TAG, "session process has already died", e); 3413 return false; 3414 } 3415 3416 IBinder clientToken = mSessionState.client.asBinder(); 3417 UserState userState = getOrCreateUserStateLocked(mSessionState.userId); 3418 ClientState clientState = userState.clientStateMap.get(clientToken); 3419 if (clientState == null) { 3420 clientState = new ClientState(clientToken, mSessionState.userId); 3421 try { 3422 clientToken.linkToDeath(clientState, 0); 3423 } catch (RemoteException e) { 3424 Slog.e(TAG, "client process has already died", e); 3425 return false; 3426 } 3427 userState.clientStateMap.put(clientToken, clientState); 3428 } 3429 clientState.sessionTokens.add(mSessionState.sessionToken); 3430 return true; 3431 } 3432 3433 @Override onChannelRetuned(Uri channelUri)3434 public void onChannelRetuned(Uri channelUri) { 3435 synchronized (mLock) { 3436 if (DEBUG) { 3437 Slog.d(TAG, "onChannelRetuned(" + channelUri + ")"); 3438 } 3439 if (mSessionState.session == null || mSessionState.client == null) { 3440 return; 3441 } 3442 try { 3443 // TODO: Consider adding this channel change in the watch log. When we do 3444 // that, how we can protect the watch log from malicious tv inputs should 3445 // be addressed. e.g. add a field which represents where the channel change 3446 // originated from. 3447 mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq); 3448 if (!mSessionState.isCurrent 3449 || !Objects.equals(mSessionState.currentChannel, channelUri)) { 3450 UserState userState = getOrCreateUserStateLocked(mSessionState.userId); 3451 mSessionState.isCurrent = true; 3452 mSessionState.currentChannel = channelUri; 3453 notifyCurrentChannelInfosUpdatedLocked(userState); 3454 } 3455 } catch (RemoteException e) { 3456 Slog.e(TAG, "error in onChannelRetuned", e); 3457 } 3458 } 3459 } 3460 3461 @Override onAudioPresentationsChanged(List<AudioPresentation> audioPresentations)3462 public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations) { 3463 synchronized (mLock) { 3464 if (DEBUG) { 3465 Slog.d(TAG, "onAudioPresentationChanged(" + audioPresentations + ")"); 3466 } 3467 if (mSessionState.session == null || mSessionState.client == null) { 3468 return; 3469 } 3470 try { 3471 mSessionState.client.onAudioPresentationsChanged(audioPresentations, 3472 mSessionState.seq); 3473 } catch (RemoteException e) { 3474 Slog.e(TAG, "error in onAudioPresentationsChanged", e); 3475 } 3476 } 3477 } 3478 3479 @Override onAudioPresentationSelected(int presentationId, int programId)3480 public void onAudioPresentationSelected(int presentationId, int programId) { 3481 synchronized (mLock) { 3482 if (DEBUG) { 3483 Slog.d(TAG, "onAudioPresentationSelected(presentationId=" + presentationId 3484 + ", programId=" + programId + ")"); 3485 } 3486 if (mSessionState.session == null || mSessionState.client == null) { 3487 return; 3488 } 3489 try { 3490 mSessionState.client.onAudioPresentationSelected(presentationId, programId, 3491 mSessionState.seq); 3492 } catch (RemoteException e) { 3493 Slog.e(TAG, "error in onAudioPresentationSelected", e); 3494 } 3495 } 3496 } 3497 3498 @Override onTracksChanged(List<TvTrackInfo> tracks)3499 public void onTracksChanged(List<TvTrackInfo> tracks) { 3500 synchronized (mLock) { 3501 if (DEBUG) { 3502 Slog.d(TAG, "onTracksChanged(" + tracks + ")"); 3503 } 3504 if (mSessionState.session == null || mSessionState.client == null) { 3505 return; 3506 } 3507 try { 3508 mSessionState.client.onTracksChanged(tracks, mSessionState.seq); 3509 } catch (RemoteException e) { 3510 Slog.e(TAG, "error in onTracksChanged", e); 3511 } 3512 } 3513 } 3514 3515 @Override onTrackSelected(int type, String trackId)3516 public void onTrackSelected(int type, String trackId) { 3517 synchronized (mLock) { 3518 if (DEBUG) { 3519 Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")"); 3520 } 3521 if (mSessionState.session == null || mSessionState.client == null) { 3522 return; 3523 } 3524 try { 3525 mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq); 3526 } catch (RemoteException e) { 3527 Slog.e(TAG, "error in onTrackSelected", e); 3528 } 3529 } 3530 } 3531 3532 @Override onVideoAvailable()3533 public void onVideoAvailable() { 3534 synchronized (mLock) { 3535 if (DEBUG) { 3536 Slog.d(TAG, "onVideoAvailable()"); 3537 } 3538 if (mSessionState.session == null || mSessionState.client == null) { 3539 return; 3540 } 3541 TvInputState tvInputState = getTvInputState(mSessionState, 3542 getUserStateLocked(mCurrentUserId)); 3543 try { 3544 mSessionState.client.onVideoAvailable(mSessionState.seq); 3545 logTuneStateChanged( 3546 FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE, 3547 mSessionState, tvInputState); 3548 } catch (RemoteException e) { 3549 Slog.e(TAG, "error in onVideoAvailable", e); 3550 } 3551 } 3552 } 3553 3554 @Override onVideoUnavailable(int reason)3555 public void onVideoUnavailable(int reason) { 3556 synchronized (mLock) { 3557 if (DEBUG) { 3558 Slog.d(TAG, "onVideoUnavailable(" + reason + ")"); 3559 } 3560 if (mSessionState.session == null || mSessionState.client == null) { 3561 return; 3562 } 3563 TvInputState tvInputState = getTvInputState(mSessionState, 3564 getUserStateLocked(mCurrentUserId)); 3565 try { 3566 mSessionState.client.onVideoUnavailable(reason, mSessionState.seq); 3567 int loggedReason = getVideoUnavailableReasonForStatsd(reason); 3568 logTuneStateChanged(loggedReason, mSessionState, tvInputState); 3569 } catch (RemoteException e) { 3570 Slog.e(TAG, "error in onVideoUnavailable", e); 3571 } 3572 } 3573 } 3574 3575 @Override onContentAllowed()3576 public void onContentAllowed() { 3577 synchronized (mLock) { 3578 if (DEBUG) { 3579 Slog.d(TAG, "onContentAllowed()"); 3580 } 3581 if (mSessionState.session == null || mSessionState.client == null) { 3582 return; 3583 } 3584 try { 3585 mSessionState.client.onContentAllowed(mSessionState.seq); 3586 } catch (RemoteException e) { 3587 Slog.e(TAG, "error in onContentAllowed", e); 3588 } 3589 } 3590 } 3591 3592 @Override onContentBlocked(String rating)3593 public void onContentBlocked(String rating) { 3594 synchronized (mLock) { 3595 if (DEBUG) { 3596 Slog.d(TAG, "onContentBlocked()"); 3597 } 3598 if (mSessionState.session == null || mSessionState.client == null) { 3599 return; 3600 } 3601 try { 3602 mSessionState.client.onContentBlocked(rating, mSessionState.seq); 3603 } catch (RemoteException e) { 3604 Slog.e(TAG, "error in onContentBlocked", e); 3605 } 3606 } 3607 } 3608 3609 @Override onLayoutSurface(int left, int top, int right, int bottom)3610 public void onLayoutSurface(int left, int top, int right, int bottom) { 3611 synchronized (mLock) { 3612 if (DEBUG) { 3613 Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top 3614 + ", right=" + right + ", bottom=" + bottom + ",)"); 3615 } 3616 if (mSessionState.session == null || mSessionState.client == null) { 3617 return; 3618 } 3619 try { 3620 mSessionState.client.onLayoutSurface(left, top, right, bottom, 3621 mSessionState.seq); 3622 } catch (RemoteException e) { 3623 Slog.e(TAG, "error in onLayoutSurface", e); 3624 } 3625 } 3626 } 3627 3628 @Override onSessionEvent(String eventType, Bundle eventArgs)3629 public void onSessionEvent(String eventType, Bundle eventArgs) { 3630 synchronized (mLock) { 3631 if (DEBUG) { 3632 Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs 3633 + ")"); 3634 } 3635 if (mSessionState.session == null || mSessionState.client == null) { 3636 return; 3637 } 3638 try { 3639 mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq); 3640 } catch (RemoteException e) { 3641 Slog.e(TAG, "error in onSessionEvent", e); 3642 } 3643 } 3644 } 3645 3646 @Override onTimeShiftStatusChanged(int status)3647 public void onTimeShiftStatusChanged(int status) { 3648 synchronized (mLock) { 3649 if (DEBUG) { 3650 Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")"); 3651 } 3652 if (mSessionState.session == null || mSessionState.client == null) { 3653 return; 3654 } 3655 try { 3656 mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq); 3657 } catch (RemoteException e) { 3658 Slog.e(TAG, "error in onTimeShiftStatusChanged", e); 3659 } 3660 } 3661 } 3662 3663 @Override onTimeShiftStartPositionChanged(long timeMs)3664 public void onTimeShiftStartPositionChanged(long timeMs) { 3665 synchronized (mLock) { 3666 if (DEBUG) { 3667 Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")"); 3668 } 3669 if (mSessionState.session == null || mSessionState.client == null) { 3670 return; 3671 } 3672 try { 3673 mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq); 3674 } catch (RemoteException e) { 3675 Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e); 3676 } 3677 } 3678 } 3679 3680 @Override onTimeShiftCurrentPositionChanged(long timeMs)3681 public void onTimeShiftCurrentPositionChanged(long timeMs) { 3682 synchronized (mLock) { 3683 if (DEBUG) { 3684 Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")"); 3685 } 3686 if (mSessionState.session == null || mSessionState.client == null) { 3687 return; 3688 } 3689 try { 3690 mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs, 3691 mSessionState.seq); 3692 } catch (RemoteException e) { 3693 Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e); 3694 } 3695 } 3696 } 3697 3698 @Override onAitInfoUpdated(AitInfo aitInfo)3699 public void onAitInfoUpdated(AitInfo aitInfo) { 3700 synchronized (mLock) { 3701 if (DEBUG) { 3702 Slog.d(TAG, "onAitInfoUpdated(" + aitInfo + ")"); 3703 } 3704 if (mSessionState.session == null || mSessionState.client == null) { 3705 return; 3706 } 3707 try { 3708 mSessionState.client.onAitInfoUpdated(aitInfo, mSessionState.seq); 3709 } catch (RemoteException e) { 3710 Slog.e(TAG, "error in onAitInfoUpdated", e); 3711 } 3712 } 3713 } 3714 3715 @Override onSignalStrength(int strength)3716 public void onSignalStrength(int strength) { 3717 synchronized (mLock) { 3718 if (DEBUG) { 3719 Slog.d(TAG, "onSignalStrength(" + strength + ")"); 3720 } 3721 if (mSessionState.session == null || mSessionState.client == null) { 3722 return; 3723 } 3724 try { 3725 mSessionState.client.onSignalStrength(strength, mSessionState.seq); 3726 } catch (RemoteException e) { 3727 Slog.e(TAG, "error in onSignalStrength", e); 3728 } 3729 } 3730 } 3731 3732 @Override onCueingMessageAvailability(boolean available)3733 public void onCueingMessageAvailability(boolean available) { 3734 synchronized (mLock) { 3735 if (DEBUG) { 3736 Slog.d(TAG, "onCueingMessageAvailability(" + available + ")"); 3737 } 3738 if (mSessionState.session == null || mSessionState.client == null) { 3739 return; 3740 } 3741 try { 3742 mSessionState.client.onCueingMessageAvailability(available, mSessionState.seq); 3743 } catch (RemoteException e) { 3744 Slog.e(TAG, "error in onCueingMessageAvailability", e); 3745 } 3746 } 3747 } 3748 3749 @Override onTimeShiftMode(int mode)3750 public void onTimeShiftMode(int mode) { 3751 synchronized (mLock) { 3752 if (DEBUG) { 3753 Slog.d(TAG, "onTimeShiftMode(" + mode + ")"); 3754 } 3755 if (mSessionState.session == null || mSessionState.client == null) { 3756 return; 3757 } 3758 try { 3759 mSessionState.client.onTimeShiftMode(mode, mSessionState.seq); 3760 } catch (RemoteException e) { 3761 Slog.e(TAG, "error in onTimeShiftMode", e); 3762 } 3763 } 3764 } 3765 3766 @Override onAvailableSpeeds(float[] speeds)3767 public void onAvailableSpeeds(float[] speeds) { 3768 synchronized (mLock) { 3769 if (DEBUG) { 3770 Slog.d(TAG, "onAvailableSpeeds(" + Arrays.toString(speeds) + ")"); 3771 } 3772 if (mSessionState.session == null || mSessionState.client == null) { 3773 return; 3774 } 3775 try { 3776 mSessionState.client.onAvailableSpeeds(speeds, mSessionState.seq); 3777 } catch (RemoteException e) { 3778 Slog.e(TAG, "error in onAvailableSpeeds", e); 3779 } 3780 } 3781 } 3782 3783 @Override onTuned(Uri channelUri)3784 public void onTuned(Uri channelUri) { 3785 synchronized (mLock) { 3786 if (DEBUG) { 3787 Slog.d(TAG, "onTuned()"); 3788 } 3789 if (mSessionState.session == null || mSessionState.client == null) { 3790 return; 3791 } 3792 try { 3793 mSessionState.client.onTuned(channelUri, mSessionState.seq); 3794 } catch (RemoteException e) { 3795 Slog.e(TAG, "error in onTuned", e); 3796 } 3797 } 3798 } 3799 3800 @Override onTvMessage(int type, Bundle data)3801 public void onTvMessage(int type, Bundle data) { 3802 synchronized (mLock) { 3803 if (DEBUG) { 3804 Slog.d(TAG, "onTvMessage(type=" + type + ", data=" + data + ")"); 3805 } 3806 if (mSessionState.session == null || mSessionState.client == null) { 3807 return; 3808 } 3809 try { 3810 mSessionState.client.onTvMessage(type, data, mSessionState.seq); 3811 } catch (RemoteException e) { 3812 Slog.e(TAG, "error in onTvMessage", e); 3813 } 3814 } 3815 } 3816 3817 // For the recording session only 3818 @Override onRecordingStopped(Uri recordedProgramUri)3819 public void onRecordingStopped(Uri recordedProgramUri) { 3820 synchronized (mLock) { 3821 if (DEBUG) { 3822 Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri 3823 + ")"); 3824 } 3825 if (mSessionState.session == null || mSessionState.client == null) { 3826 return; 3827 } 3828 try { 3829 mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq); 3830 } catch (RemoteException e) { 3831 Slog.e(TAG, "error in onRecordingStopped", e); 3832 } 3833 } 3834 } 3835 3836 // For the recording session only 3837 @Override onError(int error)3838 public void onError(int error) { 3839 synchronized (mLock) { 3840 if (DEBUG) { 3841 Slog.d(TAG, "onError(error=" + error + ")"); 3842 } 3843 if (mSessionState.session == null || mSessionState.client == null) { 3844 return; 3845 } 3846 try { 3847 mSessionState.client.onError(error, mSessionState.seq); 3848 } catch (RemoteException e) { 3849 Slog.e(TAG, "error in onError", e); 3850 } 3851 } 3852 } 3853 3854 @Override onBroadcastInfoResponse(BroadcastInfoResponse response)3855 public void onBroadcastInfoResponse(BroadcastInfoResponse response) { 3856 synchronized (mLock) { 3857 if (DEBUG) { 3858 Slog.d(TAG, "onBroadcastInfoResponse()"); 3859 } 3860 if (mSessionState.session == null || mSessionState.client == null) { 3861 return; 3862 } 3863 try { 3864 mSessionState.client.onBroadcastInfoResponse(response, mSessionState.seq); 3865 } catch (RemoteException e) { 3866 Slog.e(TAG, "error in onBroadcastInfoResponse", e); 3867 } 3868 } 3869 } 3870 3871 @Override onAdResponse(AdResponse response)3872 public void onAdResponse(AdResponse response) { 3873 synchronized (mLock) { 3874 if (DEBUG) { 3875 Slog.d(TAG, "onAdResponse()"); 3876 } 3877 if (mSessionState.session == null || mSessionState.client == null) { 3878 return; 3879 } 3880 try { 3881 mSessionState.client.onAdResponse(response, mSessionState.seq); 3882 } catch (RemoteException e) { 3883 Slog.e(TAG, "error in onAdResponse", e); 3884 } 3885 } 3886 } 3887 3888 @Override onAdBufferConsumed(AdBuffer buffer)3889 public void onAdBufferConsumed(AdBuffer buffer) { 3890 synchronized (mLock) { 3891 if (DEBUG) { 3892 Slog.d(TAG, "onAdBufferConsumed()"); 3893 } 3894 if (mSessionState.session == null || mSessionState.client == null) { 3895 return; 3896 } 3897 try { 3898 mSessionState.client.onAdBufferConsumed(buffer, mSessionState.seq); 3899 } catch (RemoteException e) { 3900 Slog.e(TAG, "error in onAdBufferConsumed", e); 3901 } finally { 3902 if (buffer != null) { 3903 buffer.getSharedMemory().close(); 3904 } 3905 } 3906 } 3907 } 3908 } 3909 3910 @VisibleForTesting getVideoUnavailableReasonForStatsd( @vInputManager.VideoUnavailableReason int reason)3911 static int getVideoUnavailableReasonForStatsd( 3912 @TvInputManager.VideoUnavailableReason int reason) { 3913 int loggedReason = reason + FrameworkStatsLog 3914 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; 3915 if (loggedReason < FrameworkStatsLog 3916 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN 3917 || loggedReason > FrameworkStatsLog 3918 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) { 3919 loggedReason = FrameworkStatsLog 3920 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; 3921 } 3922 return loggedReason; 3923 } 3924 getUserStateLocked(int userId)3925 private UserState getUserStateLocked(int userId) { 3926 return mUserStates.get(userId); 3927 } 3928 3929 private static final class WatchLogHandler extends Handler { 3930 // There are only two kinds of watch events that can happen on the system: 3931 // 1. The current TV input session is tuned to a new channel. 3932 // 2. The session is released for some reason. 3933 // The former indicates the end of the previous log entry, if any, followed by the start of 3934 // a new entry. The latter indicates the end of the most recent entry for the given session. 3935 // Here the system supplies the database the smallest set of information only that is 3936 // sufficient to consolidate the log entries while minimizing database operations in the 3937 // system service. 3938 static final int MSG_LOG_WATCH_START = 1; 3939 static final int MSG_LOG_WATCH_END = 2; 3940 static final int MSG_SWITCH_CONTENT_RESOLVER = 3; 3941 3942 private ContentResolver mContentResolver; 3943 WatchLogHandler(ContentResolver contentResolver, Looper looper)3944 WatchLogHandler(ContentResolver contentResolver, Looper looper) { 3945 super(looper); 3946 mContentResolver = contentResolver; 3947 } 3948 3949 @Override handleMessage(Message msg)3950 public void handleMessage(Message msg) { 3951 switch (msg.what) { 3952 case MSG_LOG_WATCH_START: { 3953 SomeArgs args = (SomeArgs) msg.obj; 3954 String packageName = (String) args.arg1; 3955 long watchStartTime = (long) args.arg2; 3956 long channelId = (long) args.arg3; 3957 Bundle tuneParams = (Bundle) args.arg4; 3958 IBinder sessionToken = (IBinder) args.arg5; 3959 3960 ContentValues values = new ContentValues(); 3961 values.put(TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME, packageName); 3962 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 3963 watchStartTime); 3964 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 3965 if (tuneParams != null) { 3966 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS, 3967 encodeTuneParams(tuneParams)); 3968 } 3969 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN, 3970 sessionToken.toString()); 3971 3972 try{ 3973 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 3974 }catch(IllegalArgumentException ex){ 3975 Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_START", ex); 3976 } 3977 args.recycle(); 3978 break; 3979 } 3980 case MSG_LOG_WATCH_END: { 3981 SomeArgs args = (SomeArgs) msg.obj; 3982 IBinder sessionToken = (IBinder) args.arg1; 3983 long watchEndTime = (long) args.arg2; 3984 3985 ContentValues values = new ContentValues(); 3986 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 3987 watchEndTime); 3988 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN, 3989 sessionToken.toString()); 3990 3991 try{ 3992 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 3993 }catch(IllegalArgumentException ex){ 3994 Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_END", ex); 3995 } 3996 args.recycle(); 3997 break; 3998 } 3999 case MSG_SWITCH_CONTENT_RESOLVER: { 4000 mContentResolver = (ContentResolver) msg.obj; 4001 break; 4002 } 4003 default: { 4004 Slog.w(TAG, "unhandled message code: " + msg.what); 4005 break; 4006 } 4007 } 4008 } 4009 encodeTuneParams(Bundle tuneParams)4010 private String encodeTuneParams(Bundle tuneParams) { 4011 StringBuilder builder = new StringBuilder(); 4012 Set<String> keySet = tuneParams.keySet(); 4013 Iterator<String> it = keySet.iterator(); 4014 while (it.hasNext()) { 4015 String key = it.next(); 4016 Object value = tuneParams.get(key); 4017 if (value == null) { 4018 continue; 4019 } 4020 builder.append(replaceEscapeCharacters(key)); 4021 builder.append("="); 4022 builder.append(replaceEscapeCharacters(value.toString())); 4023 if (it.hasNext()) { 4024 builder.append(", "); 4025 } 4026 } 4027 return builder.toString(); 4028 } 4029 replaceEscapeCharacters(String src)4030 private String replaceEscapeCharacters(String src) { 4031 final char ESCAPE_CHARACTER = '%'; 4032 final String ENCODING_TARGET_CHARACTERS = "%=,"; 4033 StringBuilder builder = new StringBuilder(); 4034 for (char ch : src.toCharArray()) { 4035 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) { 4036 builder.append(ESCAPE_CHARACTER); 4037 } 4038 builder.append(ch); 4039 } 4040 return builder.toString(); 4041 } 4042 } 4043 4044 private final class HardwareListener implements TvInputHardwareManager.Listener { 4045 @Override onStateChanged(String inputId, int state)4046 public void onStateChanged(String inputId, int state) { 4047 synchronized (mLock) { 4048 setStateLocked(inputId, state, mCurrentUserId); 4049 } 4050 } 4051 4052 @Override onHardwareDeviceAdded(TvInputHardwareInfo info)4053 public void onHardwareDeviceAdded(TvInputHardwareInfo info) { 4054 synchronized (mLock) { 4055 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 4056 // Broadcast the event to all hardware inputs. 4057 for (ServiceState serviceState : userState.serviceStateMap.values()) { 4058 if (!serviceState.isHardware || serviceState.service == null) continue; 4059 try { 4060 serviceState.service.notifyHardwareAdded(info); 4061 } catch (RemoteException e) { 4062 Slog.e(TAG, "error in notifyHardwareAdded", e); 4063 } 4064 } 4065 } 4066 } 4067 4068 @Override onHardwareDeviceRemoved(TvInputHardwareInfo info)4069 public void onHardwareDeviceRemoved(TvInputHardwareInfo info) { 4070 synchronized (mLock) { 4071 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 4072 // Broadcast the event to all hardware inputs. 4073 for (ServiceState serviceState : userState.serviceStateMap.values()) { 4074 if (!serviceState.isHardware || serviceState.service == null) continue; 4075 try { 4076 serviceState.service.notifyHardwareRemoved(info); 4077 } catch (RemoteException e) { 4078 Slog.e(TAG, "error in notifyHardwareRemoved", e); 4079 } 4080 } 4081 } 4082 } 4083 4084 @Override onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo)4085 public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { 4086 synchronized (mLock) { 4087 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 4088 // Broadcast the event to all hardware inputs. 4089 for (ServiceState serviceState : userState.serviceStateMap.values()) { 4090 if (!serviceState.isHardware || serviceState.service == null) continue; 4091 try { 4092 serviceState.service.notifyHdmiDeviceAdded(deviceInfo); 4093 } catch (RemoteException e) { 4094 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 4095 } 4096 } 4097 } 4098 } 4099 4100 @Override onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo)4101 public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { 4102 synchronized (mLock) { 4103 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 4104 // Broadcast the event to all hardware inputs. 4105 for (ServiceState serviceState : userState.serviceStateMap.values()) { 4106 if (!serviceState.isHardware || serviceState.service == null) continue; 4107 try { 4108 serviceState.service.notifyHdmiDeviceRemoved(deviceInfo); 4109 } catch (RemoteException e) { 4110 Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e); 4111 } 4112 } 4113 } 4114 } 4115 4116 @Override onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo)4117 public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) { 4118 synchronized (mLock) { 4119 Integer state; 4120 switch (deviceInfo.getDevicePowerStatus()) { 4121 case HdmiControlManager.POWER_STATUS_ON: 4122 state = INPUT_STATE_CONNECTED; 4123 break; 4124 case HdmiControlManager.POWER_STATUS_STANDBY: 4125 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON: 4126 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY: 4127 state = INPUT_STATE_CONNECTED_STANDBY; 4128 break; 4129 case HdmiControlManager.POWER_STATUS_UNKNOWN: 4130 default: 4131 state = null; 4132 break; 4133 } 4134 if (state != null) { 4135 setStateLocked(inputId, state, mCurrentUserId); 4136 } 4137 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 4138 // Broadcast the event to all hardware inputs. 4139 for (ServiceState serviceState : userState.serviceStateMap.values()) { 4140 if (!serviceState.isHardware || serviceState.service == null) continue; 4141 try { 4142 serviceState.service.notifyHdmiDeviceUpdated(deviceInfo); 4143 } catch (RemoteException e) { 4144 Slog.e(TAG, "error in notifyHdmiDeviceUpdated", e); 4145 } 4146 } 4147 } 4148 } 4149 4150 @Override onTvMessage(String inputId, int type, Bundle data)4151 public void onTvMessage(String inputId, int type, Bundle data) { 4152 synchronized (mLock) { 4153 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 4154 TvInputState inputState = userState.inputMap.get(inputId); 4155 if (inputState == null) { 4156 Slog.e(TAG, "failed to send TV message - unknown input id " + inputId); 4157 return; 4158 } 4159 ServiceState serviceState = userState.serviceStateMap.get(inputState.info 4160 .getComponent()); 4161 for (IBinder token : serviceState.sessionTokens) { 4162 try { 4163 SessionState sessionState = getSessionStateLocked(token, 4164 Binder.getCallingUid(), mCurrentUserId); 4165 if (!sessionState.isRecordingSession 4166 && sessionState.hardwareSessionToken != null) { 4167 sessionState.session.notifyTvMessage(type, data); 4168 } 4169 } catch (RemoteException e) { 4170 Slog.e(TAG, "error in onTvMessage", e); 4171 } 4172 } 4173 } 4174 } 4175 } 4176 4177 private static class SessionNotFoundException extends IllegalArgumentException { SessionNotFoundException(String name)4178 public SessionNotFoundException(String name) { 4179 super(name); 4180 } 4181 } 4182 4183 private static class ClientPidNotFoundException extends IllegalArgumentException { ClientPidNotFoundException(String name)4184 public ClientPidNotFoundException(String name) { 4185 super(name); 4186 } 4187 } 4188 } 4189