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