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 long identity = Binder.clearCallingIdentity(); 2308 final int callingUid = Binder.getCallingUid(); 2309 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 2310 userId, "requestChannelBrowsable"); 2311 try { 2312 Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED); 2313 List<ResolveInfo> list = getContext().getPackageManager() 2314 .queryBroadcastReceivers(intent, 0); 2315 if (list != null) { 2316 for (ResolveInfo info : list) { 2317 String receiverPackageName = info.activityInfo.packageName; 2318 intent.putExtra(TvContract.EXTRA_CHANNEL_ID, ContentUris.parseId( 2319 channelUri)); 2320 intent.putExtra(TvContract.EXTRA_PACKAGE_NAME, callingPackageName); 2321 intent.setPackage(receiverPackageName); 2322 getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId)); 2323 } 2324 } 2325 } finally { 2326 Binder.restoreCallingIdentity(identity); 2327 } 2328 } 2329 2330 @Override getClientPid(String sessionId)2331 public int getClientPid(String sessionId) { 2332 ensureTunerResourceAccessPermission(); 2333 final long identity = Binder.clearCallingIdentity(); 2334 2335 int clientPid = TvInputManager.UNKNOWN_CLIENT_PID; 2336 try { 2337 synchronized (mLock) { 2338 try { 2339 clientPid = getClientPidLocked(sessionId); 2340 } catch (ClientPidNotFoundException e) { 2341 Slog.e(TAG, "error in getClientPid", e); 2342 } 2343 } 2344 } finally { 2345 Binder.restoreCallingIdentity(identity); 2346 } 2347 return clientPid; 2348 } 2349 2350 @Override getCurrentTunedInfos(@serIdInt int userId)2351 public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) { 2352 if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO) 2353 != PackageManager.PERMISSION_GRANTED) { 2354 throw new SecurityException( 2355 "The caller does not have access tuned info permission"); 2356 } 2357 int callingPid = Binder.getCallingPid(); 2358 int callingUid = Binder.getCallingUid(); 2359 final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, 2360 "getTvCurrentChannelInfos"); 2361 synchronized (mLock) { 2362 UserState userState = getOrCreateUserStateLocked(resolvedUserId); 2363 return getCurrentTunedInfosInternalLocked(userState, callingPid, callingUid); 2364 } 2365 } 2366 2367 /** 2368 * Add a hardware device in the TvInputHardwareManager for CTS testing 2369 * purpose. 2370 * 2371 * @param deviceId the id of the adding hardware device. 2372 */ 2373 @Override addHardwareDevice(int deviceId)2374 public void addHardwareDevice(int deviceId) { 2375 TvInputHardwareInfo info = new TvInputHardwareInfo.Builder() 2376 .deviceId(deviceId) 2377 .type(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) 2378 .audioType(DEVICE_NONE) 2379 .audioAddress("0") 2380 .hdmiPortId(0) 2381 .build(); 2382 mTvInputHardwareManager.onDeviceAvailable(info, null); 2383 } 2384 2385 /** 2386 * Remove a hardware device in the TvInputHardwareManager for CTS testing 2387 * purpose. 2388 * 2389 * @param deviceId the id of the removing hardware device. 2390 */ 2391 @Override removeHardwareDevice(int deviceId)2392 public void removeHardwareDevice(int deviceId) { 2393 mTvInputHardwareManager.onDeviceUnavailable(deviceId); 2394 } 2395 2396 @GuardedBy("mLock") getClientPidLocked(String sessionId)2397 private int getClientPidLocked(String sessionId) 2398 throws IllegalStateException { 2399 if (mSessionIdToSessionStateMap.get(sessionId) == null) { 2400 throw new IllegalStateException("Client Pid not found with sessionId " 2401 + sessionId); 2402 } 2403 return mSessionIdToSessionStateMap.get(sessionId).callingPid; 2404 } 2405 ensureTunerResourceAccessPermission()2406 private void ensureTunerResourceAccessPermission() { 2407 if (mContext.checkCallingPermission( 2408 android.Manifest.permission.TUNER_RESOURCE_ACCESS) 2409 != PackageManager.PERMISSION_GRANTED) { 2410 throw new SecurityException("Requires TUNER_RESOURCE_ACCESS permission"); 2411 } 2412 } 2413 2414 @Override 2415 @SuppressWarnings("resource") dump(FileDescriptor fd, final PrintWriter writer, String[] args)2416 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 2417 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 2418 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 2419 2420 synchronized (mLock) { 2421 pw.println("User Ids (Current user: " + mCurrentUserId + "):"); 2422 pw.increaseIndent(); 2423 for (int i = 0; i < mUserStates.size(); i++) { 2424 int userId = mUserStates.keyAt(i); 2425 pw.println(Integer.valueOf(userId)); 2426 } 2427 pw.decreaseIndent(); 2428 2429 for (int i = 0; i < mUserStates.size(); i++) { 2430 int userId = mUserStates.keyAt(i); 2431 UserState userState = getOrCreateUserStateLocked(userId); 2432 pw.println("UserState (" + userId + "):"); 2433 pw.increaseIndent(); 2434 2435 pw.println("inputMap: inputId -> TvInputState"); 2436 pw.increaseIndent(); 2437 for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) { 2438 pw.println(entry.getKey() + ": " + entry.getValue()); 2439 } 2440 pw.decreaseIndent(); 2441 2442 pw.println("packageSet:"); 2443 pw.increaseIndent(); 2444 for (String packageName : userState.packageSet) { 2445 pw.println(packageName); 2446 } 2447 pw.decreaseIndent(); 2448 2449 pw.println("clientStateMap: ITvInputClient -> ClientState"); 2450 pw.increaseIndent(); 2451 for (Map.Entry<IBinder, ClientState> entry : 2452 userState.clientStateMap.entrySet()) { 2453 ClientState client = entry.getValue(); 2454 pw.println(entry.getKey() + ": " + client); 2455 2456 pw.increaseIndent(); 2457 2458 pw.println("sessionTokens:"); 2459 pw.increaseIndent(); 2460 for (IBinder token : client.sessionTokens) { 2461 pw.println("" + token); 2462 } 2463 pw.decreaseIndent(); 2464 2465 pw.println("clientTokens: " + client.clientToken); 2466 pw.println("userId: " + client.userId); 2467 2468 pw.decreaseIndent(); 2469 } 2470 pw.decreaseIndent(); 2471 2472 pw.println("serviceStateMap: ComponentName -> ServiceState"); 2473 pw.increaseIndent(); 2474 for (Map.Entry<ComponentName, ServiceState> entry : 2475 userState.serviceStateMap.entrySet()) { 2476 ServiceState service = entry.getValue(); 2477 pw.println(entry.getKey() + ": " + service); 2478 2479 pw.increaseIndent(); 2480 2481 pw.println("sessionTokens:"); 2482 pw.increaseIndent(); 2483 for (IBinder token : service.sessionTokens) { 2484 pw.println("" + token); 2485 } 2486 pw.decreaseIndent(); 2487 2488 pw.println("service: " + service.service); 2489 pw.println("callback: " + service.callback); 2490 pw.println("bound: " + service.bound); 2491 pw.println("reconnecting: " + service.reconnecting); 2492 2493 pw.decreaseIndent(); 2494 } 2495 pw.decreaseIndent(); 2496 2497 pw.println("sessionStateMap: ITvInputSession -> SessionState"); 2498 pw.increaseIndent(); 2499 for (Map.Entry<IBinder, SessionState> entry : 2500 userState.sessionStateMap.entrySet()) { 2501 SessionState session = entry.getValue(); 2502 pw.println(entry.getKey() + ": " + session); 2503 2504 pw.increaseIndent(); 2505 pw.println("inputId: " + session.inputId); 2506 pw.println("sessionId: " + session.sessionId); 2507 pw.println("client: " + session.client); 2508 pw.println("seq: " + session.seq); 2509 pw.println("callingUid: " + session.callingUid); 2510 pw.println("callingPid: " + session.callingPid); 2511 pw.println("userId: " + session.userId); 2512 pw.println("sessionToken: " + session.sessionToken); 2513 pw.println("session: " + session.session); 2514 pw.println("hardwareSessionToken: " + session.hardwareSessionToken); 2515 pw.decreaseIndent(); 2516 } 2517 pw.decreaseIndent(); 2518 2519 pw.println("mCallbacks:"); 2520 pw.increaseIndent(); 2521 int n = userState.mCallbacks.beginBroadcast(); 2522 for (int j = 0; j < n; ++j) { 2523 pw.println(userState.mCallbacks.getRegisteredCallbackItem(j)); 2524 } 2525 userState.mCallbacks.finishBroadcast(); 2526 pw.decreaseIndent(); 2527 2528 pw.println("mainSessionToken: " + userState.mainSessionToken); 2529 pw.decreaseIndent(); 2530 } 2531 } 2532 mTvInputHardwareManager.dump(fd, writer, args); 2533 } 2534 } 2535 2536 @Nullable getTvInputState( SessionState sessionState, @Nullable UserState userState)2537 private static TvInputState getTvInputState( 2538 SessionState sessionState, 2539 @Nullable UserState userState) { 2540 if (userState != null) { 2541 return userState.inputMap.get(sessionState.inputId); 2542 } 2543 return null; 2544 } 2545 2546 @GuardedBy("mLock") getCurrentTunedInfosInternalLocked( UserState userState, int callingPid, int callingUid)2547 private List<TunedInfo> getCurrentTunedInfosInternalLocked( 2548 UserState userState, int callingPid, int callingUid) { 2549 List<TunedInfo> channelInfos = new ArrayList<>(); 2550 boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid); 2551 for (SessionState state : userState.sessionStateMap.values()) { 2552 if (state.isCurrent) { 2553 Integer appTag; 2554 int appType; 2555 if (state.callingUid == callingUid) { 2556 appTag = APP_TAG_SELF; 2557 appType = TunedInfo.APP_TYPE_SELF; 2558 } else { 2559 appTag = userState.mAppTagMap.get(state.callingUid); 2560 if (appTag == null) { 2561 appTag = userState.mNextAppTag++; 2562 userState.mAppTagMap.put(state.callingUid, appTag); 2563 } 2564 appType = isSystemApp(state.componentName.getPackageName()) 2565 ? TunedInfo.APP_TYPE_SYSTEM 2566 : TunedInfo.APP_TYPE_NON_SYSTEM; 2567 } 2568 channelInfos.add(new TunedInfo( 2569 state.inputId, 2570 watchedProgramsAccess ? state.currentChannel : null, 2571 state.isRecordingSession, 2572 state.isVisible, 2573 state.isMainSession, 2574 appType, 2575 appTag)); 2576 } 2577 } 2578 return channelInfos; 2579 } 2580 hasAccessWatchedProgramsPermission(int callingPid, int callingUid)2581 private boolean hasAccessWatchedProgramsPermission(int callingPid, int callingUid) { 2582 return mContext.checkPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS, callingPid, callingUid) 2583 == PackageManager.PERMISSION_GRANTED; 2584 } 2585 isSystemApp(String pkg)2586 private boolean isSystemApp(String pkg) { 2587 try { 2588 return (mContext.getPackageManager().getApplicationInfo(pkg, 0).flags 2589 & ApplicationInfo.FLAG_SYSTEM) != 0; 2590 } catch (NameNotFoundException e) { 2591 return false; 2592 } 2593 } 2594 2595 /** 2596 * Log Tune state changes to {@link FrameworkStatsLog}. 2597 * 2598 * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. 2599 * Inspect the code at: 2600 * 2601 * <ul> 2602 * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState 2603 * <li>{@link #logTuneStateChanged} 2604 * <li>{@link TvInputManagerService.BinderService#createSession} 2605 * <li>{@link SessionState#sessionId} 2606 * </ul> 2607 */ logTuneStateChanged(int state, SessionState sessionState, @Nullable TvInputState inputState)2608 private void logTuneStateChanged(int state, SessionState sessionState, 2609 @Nullable TvInputState inputState) { 2610 int tisUid = Process.INVALID_UID; 2611 int inputType = FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__TYPE__TIF_INPUT_TYPE_UNKNOWN; 2612 int inputId = 0; 2613 int hdmiPort = 0; 2614 if (inputState != null) { 2615 tisUid = inputState.uid; 2616 inputType = inputState.info.getType(); 2617 if (inputType == TvInputInfo.TYPE_TUNER) { 2618 inputType = FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__TYPE__TUNER; 2619 } 2620 inputId = inputState.inputNumber; 2621 HdmiDeviceInfo hdmiDeviceInfo = inputState.info.getHdmiDeviceInfo(); 2622 if (hdmiDeviceInfo != null) { 2623 hdmiPort = hdmiDeviceInfo.getPortId(); 2624 } 2625 } 2626 FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED, 2627 new int[]{sessionState.callingUid, 2628 tisUid}, 2629 new String[]{"tif_player", "tv_input_service"}, 2630 state, 2631 sessionState.sessionId, 2632 inputType, 2633 inputId, 2634 hdmiPort); 2635 } 2636 2637 private static final class UserState { 2638 // A mapping from the TV input id to its TvInputState. 2639 private Map<String, TvInputState> inputMap = new HashMap<>(); 2640 2641 // A set of all TV input packages. 2642 private final Set<String> packageSet = new HashSet<>(); 2643 2644 // A list of all TV content rating systems defined. 2645 private final List<TvContentRatingSystemInfo> 2646 contentRatingSystemList = new ArrayList<>(); 2647 2648 // A mapping from the token of a client to its state. 2649 private final Map<IBinder, ClientState> clientStateMap = new HashMap<>(); 2650 2651 // A mapping from the name of a TV input service to its state. 2652 private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>(); 2653 2654 // A mapping from the token of a TV input session to its state. 2655 private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>(); 2656 2657 // A list of callbacks. 2658 private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks = 2659 new RemoteCallbackList<>(); 2660 2661 private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap = 2662 new HashMap<>(); 2663 2664 // The token of a "main" TV input session. 2665 private IBinder mainSessionToken = null; 2666 2667 // Persistent data store for all internal settings maintained by the TV input manager 2668 // service. 2669 private final PersistentDataStore persistentDataStore; 2670 2671 @GuardedBy("TvInputManagerService.this.mLock") 2672 private final Map<Integer, Integer> mAppTagMap = new HashMap<>(); 2673 @GuardedBy("TvInputManagerService.this.mLock") 2674 private int mNextAppTag = 1; 2675 UserState(Context context, int userId)2676 private UserState(Context context, int userId) { 2677 persistentDataStore = new PersistentDataStore(context, userId); 2678 } 2679 } 2680 2681 private final class ClientState implements IBinder.DeathRecipient { 2682 private final List<IBinder> sessionTokens = new ArrayList<>(); 2683 2684 private IBinder clientToken; 2685 private final int userId; 2686 ClientState(IBinder clientToken, int userId)2687 ClientState(IBinder clientToken, int userId) { 2688 this.clientToken = clientToken; 2689 this.userId = userId; 2690 } 2691 isEmpty()2692 public boolean isEmpty() { 2693 return sessionTokens.isEmpty(); 2694 } 2695 2696 @Override binderDied()2697 public void binderDied() { 2698 synchronized (mLock) { 2699 UserState userState = getOrCreateUserStateLocked(userId); 2700 // DO NOT remove the client state of clientStateMap in this method. It will be 2701 // removed in releaseSessionLocked(). 2702 ClientState clientState = userState.clientStateMap.get(clientToken); 2703 if (clientState != null) { 2704 while (clientState.sessionTokens.size() > 0) { 2705 IBinder sessionToken = clientState.sessionTokens.get(0); 2706 releaseSessionLocked( 2707 sessionToken, Process.SYSTEM_UID, userId); 2708 // the releaseSessionLocked function may return before the sessionToken 2709 // is removed if the related sessionState is null. So need to check again 2710 // to avoid death curculation. 2711 if (clientState.sessionTokens.contains(sessionToken)) { 2712 Slog.d(TAG, "remove sessionToken " + sessionToken + " for " + clientToken); 2713 clientState.sessionTokens.remove(sessionToken); 2714 } 2715 } 2716 } 2717 clientToken = null; 2718 } 2719 } 2720 } 2721 2722 private final class ServiceState { 2723 private final List<IBinder> sessionTokens = new ArrayList<>(); 2724 private final ServiceConnection connection; 2725 private final ComponentName component; 2726 private final boolean isHardware; 2727 private final Map<String, TvInputInfo> hardwareInputMap = new HashMap<>(); 2728 2729 private ITvInputService service; 2730 private ServiceCallback callback; 2731 private boolean bound; 2732 private boolean reconnecting; 2733 ServiceState(ComponentName component, int userId)2734 private ServiceState(ComponentName component, int userId) { 2735 this.component = component; 2736 this.connection = new InputServiceConnection(component, userId); 2737 this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component); 2738 } 2739 } 2740 2741 private static final class TvInputState { 2742 2743 /** A TvInputInfo object which represents the TV input. */ 2744 private TvInputInfo info; 2745 2746 /** 2747 * ID unique to a specific TvInputService. 2748 */ 2749 private int inputNumber; 2750 2751 /** 2752 * The kernel user-ID that has been assigned to the application the TvInput is a part of. 2753 * 2754 * <p> 2755 * Currently this is not a unique ID (multiple applications can have 2756 * the same uid). 2757 */ 2758 private int uid; 2759 2760 /** 2761 * The state of TV input. 2762 * 2763 * <p> 2764 * Connected by default 2765 */ 2766 private int state = INPUT_STATE_CONNECTED; 2767 2768 @Override toString()2769 public String toString() { 2770 return "info: " + info + "; state: " + state; 2771 } 2772 } 2773 2774 private final class SessionState implements IBinder.DeathRecipient { 2775 private final String inputId; 2776 2777 /** 2778 * A randomly generated id for this this session. 2779 * 2780 * <p>This field contains no user or device reference and is large enough to be 2781 * effectively globally unique. 2782 * 2783 * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. 2784 * Inspect the code at: 2785 * 2786 * <ul> 2787 * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState 2788 * <li>{@link #logTuneStateChanged} 2789 * <li>{@link TvInputManagerService.BinderService#createSession} 2790 * <li>{@link SessionState#sessionId} 2791 * </ul> 2792 */ 2793 private final String sessionId; 2794 private final ComponentName componentName; 2795 private final boolean isRecordingSession; 2796 private final ITvInputClient client; 2797 private final int seq; 2798 /** 2799 * The {code UID} of the application that created the session. 2800 * 2801 * <p> 2802 * The application is usually the TIF Player. 2803 */ 2804 private final int callingUid; 2805 /** 2806 * The {@code PID} of the application that created the session. 2807 * 2808 * <p> 2809 * The application is usually the TIF Player. 2810 */ 2811 private final int callingPid; 2812 private final int userId; 2813 private final IBinder sessionToken; 2814 private ITvInputSession session; 2815 // Not null if this session represents an external device connected to a hardware TV input. 2816 private IBinder hardwareSessionToken; 2817 2818 private boolean isCurrent = false; 2819 private Uri currentChannel = null; 2820 private boolean isVisible = false; 2821 private boolean isMainSession = false; 2822 SessionState(IBinder sessionToken, String inputId, ComponentName componentName, boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, int callingPid, int userId, String sessionId)2823 private SessionState(IBinder sessionToken, String inputId, ComponentName componentName, 2824 boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, 2825 int callingPid, int userId, String sessionId) { 2826 this.sessionToken = sessionToken; 2827 this.inputId = inputId; 2828 this.componentName = componentName; 2829 this.isRecordingSession = isRecordingSession; 2830 this.client = client; 2831 this.seq = seq; 2832 this.callingUid = callingUid; 2833 this.callingPid = callingPid; 2834 this.userId = userId; 2835 this.sessionId = sessionId; 2836 } 2837 2838 @Override binderDied()2839 public void binderDied() { 2840 synchronized (mLock) { 2841 session = null; 2842 clearSessionAndNotifyClientLocked(this); 2843 } 2844 } 2845 } 2846 2847 private final class InputServiceConnection implements ServiceConnection { 2848 private final ComponentName mComponent; 2849 private final int mUserId; 2850 InputServiceConnection(ComponentName component, int userId)2851 private InputServiceConnection(ComponentName component, int userId) { 2852 mComponent = component; 2853 mUserId = userId; 2854 } 2855 2856 @Override onServiceConnected(ComponentName component, IBinder service)2857 public void onServiceConnected(ComponentName component, IBinder service) { 2858 if (DEBUG) { 2859 Slog.d(TAG, "onServiceConnected(component=" + component + ")"); 2860 } 2861 synchronized (mLock) { 2862 UserState userState = getUserStateLocked(mUserId); 2863 if (userState == null) { 2864 // The user was removed while connecting. 2865 mContext.unbindService(this); 2866 return; 2867 } 2868 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 2869 serviceState.service = ITvInputService.Stub.asInterface(service); 2870 2871 // Register a callback, if we need to. 2872 if (serviceState.isHardware && serviceState.callback == null) { 2873 serviceState.callback = new ServiceCallback(mComponent, mUserId); 2874 try { 2875 serviceState.service.registerCallback(serviceState.callback); 2876 } catch (RemoteException e) { 2877 Slog.e(TAG, "error in registerCallback", e); 2878 } 2879 } 2880 2881 List<IBinder> tokensToBeRemoved = new ArrayList<>(); 2882 2883 // And create sessions, if any. 2884 for (IBinder sessionToken : serviceState.sessionTokens) { 2885 if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) { 2886 tokensToBeRemoved.add(sessionToken); 2887 } 2888 } 2889 2890 for (IBinder sessionToken : tokensToBeRemoved) { 2891 removeSessionStateLocked(sessionToken, mUserId); 2892 } 2893 2894 for (TvInputState inputState : userState.inputMap.values()) { 2895 if (inputState.info.getComponent().equals(component) 2896 && inputState.state != INPUT_STATE_CONNECTED) { 2897 notifyInputStateChangedLocked(userState, inputState.info.getId(), 2898 inputState.state, null); 2899 } 2900 } 2901 2902 if (serviceState.isHardware) { 2903 serviceState.hardwareInputMap.clear(); 2904 for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) { 2905 try { 2906 serviceState.service.notifyHardwareAdded(hardware); 2907 } catch (RemoteException e) { 2908 Slog.e(TAG, "error in notifyHardwareAdded", e); 2909 } 2910 } 2911 for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) { 2912 try { 2913 serviceState.service.notifyHdmiDeviceAdded(device); 2914 } catch (RemoteException e) { 2915 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 2916 } 2917 } 2918 } 2919 } 2920 } 2921 2922 @Override onServiceDisconnected(ComponentName component)2923 public void onServiceDisconnected(ComponentName component) { 2924 if (DEBUG) { 2925 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")"); 2926 } 2927 if (!mComponent.equals(component)) { 2928 throw new IllegalArgumentException("Mismatched ComponentName: " 2929 + mComponent + " (expected), " + component + " (actual)."); 2930 } 2931 synchronized (mLock) { 2932 UserState userState = getOrCreateUserStateLocked(mUserId); 2933 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 2934 if (serviceState != null) { 2935 serviceState.reconnecting = true; 2936 serviceState.bound = false; 2937 serviceState.service = null; 2938 serviceState.callback = null; 2939 2940 abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId); 2941 } 2942 } 2943 } 2944 } 2945 2946 private final class ServiceCallback extends ITvInputServiceCallback.Stub { 2947 private final ComponentName mComponent; 2948 private final int mUserId; 2949 ServiceCallback(ComponentName component, int userId)2950 ServiceCallback(ComponentName component, int userId) { 2951 mComponent = component; 2952 mUserId = userId; 2953 } 2954 ensureHardwarePermission()2955 private void ensureHardwarePermission() { 2956 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 2957 != PackageManager.PERMISSION_GRANTED) { 2958 throw new SecurityException("The caller does not have hardware permission"); 2959 } 2960 } 2961 ensureValidInput(TvInputInfo inputInfo)2962 private void ensureValidInput(TvInputInfo inputInfo) { 2963 if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) { 2964 throw new IllegalArgumentException("Invalid TvInputInfo"); 2965 } 2966 } 2967 2968 @GuardedBy("mLock") addHardwareInputLocked(TvInputInfo inputInfo)2969 private void addHardwareInputLocked(TvInputInfo inputInfo) { 2970 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 2971 serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo); 2972 buildTvInputListLocked(mUserId, null); 2973 } 2974 addHardwareInput(int deviceId, TvInputInfo inputInfo)2975 public void addHardwareInput(int deviceId, TvInputInfo inputInfo) { 2976 ensureHardwarePermission(); 2977 ensureValidInput(inputInfo); 2978 synchronized (mLock) { 2979 mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo); 2980 addHardwareInputLocked(inputInfo); 2981 } 2982 } 2983 addHdmiInput(int id, TvInputInfo inputInfo)2984 public void addHdmiInput(int id, TvInputInfo inputInfo) { 2985 ensureHardwarePermission(); 2986 ensureValidInput(inputInfo); 2987 synchronized (mLock) { 2988 mTvInputHardwareManager.addHdmiInput(id, inputInfo); 2989 addHardwareInputLocked(inputInfo); 2990 } 2991 } 2992 removeHardwareInput(String inputId)2993 public void removeHardwareInput(String inputId) { 2994 ensureHardwarePermission(); 2995 synchronized (mLock) { 2996 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 2997 boolean removed = serviceState.hardwareInputMap.remove(inputId) != null; 2998 if (removed) { 2999 buildTvInputListLocked(mUserId, null); 3000 mTvInputHardwareManager.removeHardwareInput(inputId); 3001 } else { 3002 Slog.e(TAG, "failed to remove input " + inputId); 3003 } 3004 } 3005 } 3006 } 3007 3008 private final class SessionCallback extends ITvInputSessionCallback.Stub { 3009 private final SessionState mSessionState; 3010 private final InputChannel[] mChannels; 3011 SessionCallback(SessionState sessionState, InputChannel[] channels)3012 SessionCallback(SessionState sessionState, InputChannel[] channels) { 3013 mSessionState = sessionState; 3014 mChannels = channels; 3015 } 3016 3017 @Override onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken)3018 public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) { 3019 if (DEBUG) { 3020 Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")"); 3021 } 3022 synchronized (mLock) { 3023 mSessionState.session = session; 3024 mSessionState.hardwareSessionToken = hardwareSessionToken; 3025 if (session != null && addSessionTokenToClientStateLocked(session)) { 3026 sendSessionTokenToClientLocked(mSessionState.client, 3027 mSessionState.inputId, mSessionState.sessionToken, mChannels[0], 3028 mSessionState.seq); 3029 } else { 3030 removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId); 3031 sendSessionTokenToClientLocked(mSessionState.client, 3032 mSessionState.inputId, null, null, mSessionState.seq); 3033 } 3034 mChannels[0].dispose(); 3035 } 3036 } 3037 3038 @GuardedBy("mLock") addSessionTokenToClientStateLocked(ITvInputSession session)3039 private boolean addSessionTokenToClientStateLocked(ITvInputSession session) { 3040 try { 3041 session.asBinder().linkToDeath(mSessionState, 0); 3042 } catch (RemoteException e) { 3043 Slog.e(TAG, "session process has already died", e); 3044 return false; 3045 } 3046 3047 IBinder clientToken = mSessionState.client.asBinder(); 3048 UserState userState = getOrCreateUserStateLocked(mSessionState.userId); 3049 ClientState clientState = userState.clientStateMap.get(clientToken); 3050 if (clientState == null) { 3051 clientState = new ClientState(clientToken, mSessionState.userId); 3052 try { 3053 clientToken.linkToDeath(clientState, 0); 3054 } catch (RemoteException e) { 3055 Slog.e(TAG, "client process has already died", e); 3056 return false; 3057 } 3058 userState.clientStateMap.put(clientToken, clientState); 3059 } 3060 clientState.sessionTokens.add(mSessionState.sessionToken); 3061 return true; 3062 } 3063 3064 @Override onChannelRetuned(Uri channelUri)3065 public void onChannelRetuned(Uri channelUri) { 3066 synchronized (mLock) { 3067 if (DEBUG) { 3068 Slog.d(TAG, "onChannelRetuned(" + channelUri + ")"); 3069 } 3070 if (mSessionState.session == null || mSessionState.client == null) { 3071 return; 3072 } 3073 try { 3074 // TODO: Consider adding this channel change in the watch log. When we do 3075 // that, how we can protect the watch log from malicious tv inputs should 3076 // be addressed. e.g. add a field which represents where the channel change 3077 // originated from. 3078 mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq); 3079 if (!mSessionState.isCurrent 3080 || !Objects.equals(mSessionState.currentChannel, channelUri)) { 3081 UserState userState = getOrCreateUserStateLocked(mSessionState.userId); 3082 mSessionState.isCurrent = true; 3083 mSessionState.currentChannel = channelUri; 3084 notifyCurrentChannelInfosUpdatedLocked(userState); 3085 } 3086 } catch (RemoteException e) { 3087 Slog.e(TAG, "error in onChannelRetuned", e); 3088 } 3089 } 3090 } 3091 3092 @Override onTracksChanged(List<TvTrackInfo> tracks)3093 public void onTracksChanged(List<TvTrackInfo> tracks) { 3094 synchronized (mLock) { 3095 if (DEBUG) { 3096 Slog.d(TAG, "onTracksChanged(" + tracks + ")"); 3097 } 3098 if (mSessionState.session == null || mSessionState.client == null) { 3099 return; 3100 } 3101 try { 3102 mSessionState.client.onTracksChanged(tracks, mSessionState.seq); 3103 } catch (RemoteException e) { 3104 Slog.e(TAG, "error in onTracksChanged", e); 3105 } 3106 } 3107 } 3108 3109 @Override onTrackSelected(int type, String trackId)3110 public void onTrackSelected(int type, String trackId) { 3111 synchronized (mLock) { 3112 if (DEBUG) { 3113 Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")"); 3114 } 3115 if (mSessionState.session == null || mSessionState.client == null) { 3116 return; 3117 } 3118 try { 3119 mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq); 3120 } catch (RemoteException e) { 3121 Slog.e(TAG, "error in onTrackSelected", e); 3122 } 3123 } 3124 } 3125 3126 @Override onVideoAvailable()3127 public void onVideoAvailable() { 3128 synchronized (mLock) { 3129 if (DEBUG) { 3130 Slog.d(TAG, "onVideoAvailable()"); 3131 } 3132 if (mSessionState.session == null || mSessionState.client == null) { 3133 return; 3134 } 3135 TvInputState tvInputState = getTvInputState(mSessionState, 3136 getUserStateLocked(mCurrentUserId)); 3137 try { 3138 mSessionState.client.onVideoAvailable(mSessionState.seq); 3139 logTuneStateChanged( 3140 FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE, 3141 mSessionState, tvInputState); 3142 } catch (RemoteException e) { 3143 Slog.e(TAG, "error in onVideoAvailable", e); 3144 } 3145 } 3146 } 3147 3148 @Override onVideoUnavailable(int reason)3149 public void onVideoUnavailable(int reason) { 3150 synchronized (mLock) { 3151 if (DEBUG) { 3152 Slog.d(TAG, "onVideoUnavailable(" + reason + ")"); 3153 } 3154 if (mSessionState.session == null || mSessionState.client == null) { 3155 return; 3156 } 3157 TvInputState tvInputState = getTvInputState(mSessionState, 3158 getUserStateLocked(mCurrentUserId)); 3159 try { 3160 mSessionState.client.onVideoUnavailable(reason, mSessionState.seq); 3161 int loggedReason = getVideoUnavailableReasonForStatsd(reason); 3162 logTuneStateChanged(loggedReason, mSessionState, tvInputState); 3163 } catch (RemoteException e) { 3164 Slog.e(TAG, "error in onVideoUnavailable", e); 3165 } 3166 } 3167 } 3168 3169 @Override onContentAllowed()3170 public void onContentAllowed() { 3171 synchronized (mLock) { 3172 if (DEBUG) { 3173 Slog.d(TAG, "onContentAllowed()"); 3174 } 3175 if (mSessionState.session == null || mSessionState.client == null) { 3176 return; 3177 } 3178 try { 3179 mSessionState.client.onContentAllowed(mSessionState.seq); 3180 } catch (RemoteException e) { 3181 Slog.e(TAG, "error in onContentAllowed", e); 3182 } 3183 } 3184 } 3185 3186 @Override onContentBlocked(String rating)3187 public void onContentBlocked(String rating) { 3188 synchronized (mLock) { 3189 if (DEBUG) { 3190 Slog.d(TAG, "onContentBlocked()"); 3191 } 3192 if (mSessionState.session == null || mSessionState.client == null) { 3193 return; 3194 } 3195 try { 3196 mSessionState.client.onContentBlocked(rating, mSessionState.seq); 3197 } catch (RemoteException e) { 3198 Slog.e(TAG, "error in onContentBlocked", e); 3199 } 3200 } 3201 } 3202 3203 @Override onLayoutSurface(int left, int top, int right, int bottom)3204 public void onLayoutSurface(int left, int top, int right, int bottom) { 3205 synchronized (mLock) { 3206 if (DEBUG) { 3207 Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top 3208 + ", right=" + right + ", bottom=" + bottom + ",)"); 3209 } 3210 if (mSessionState.session == null || mSessionState.client == null) { 3211 return; 3212 } 3213 try { 3214 mSessionState.client.onLayoutSurface(left, top, right, bottom, 3215 mSessionState.seq); 3216 } catch (RemoteException e) { 3217 Slog.e(TAG, "error in onLayoutSurface", e); 3218 } 3219 } 3220 } 3221 3222 @Override onSessionEvent(String eventType, Bundle eventArgs)3223 public void onSessionEvent(String eventType, Bundle eventArgs) { 3224 synchronized (mLock) { 3225 if (DEBUG) { 3226 Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs 3227 + ")"); 3228 } 3229 if (mSessionState.session == null || mSessionState.client == null) { 3230 return; 3231 } 3232 try { 3233 mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq); 3234 } catch (RemoteException e) { 3235 Slog.e(TAG, "error in onSessionEvent", e); 3236 } 3237 } 3238 } 3239 3240 @Override onTimeShiftStatusChanged(int status)3241 public void onTimeShiftStatusChanged(int status) { 3242 synchronized (mLock) { 3243 if (DEBUG) { 3244 Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")"); 3245 } 3246 if (mSessionState.session == null || mSessionState.client == null) { 3247 return; 3248 } 3249 try { 3250 mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq); 3251 } catch (RemoteException e) { 3252 Slog.e(TAG, "error in onTimeShiftStatusChanged", e); 3253 } 3254 } 3255 } 3256 3257 @Override onTimeShiftStartPositionChanged(long timeMs)3258 public void onTimeShiftStartPositionChanged(long timeMs) { 3259 synchronized (mLock) { 3260 if (DEBUG) { 3261 Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")"); 3262 } 3263 if (mSessionState.session == null || mSessionState.client == null) { 3264 return; 3265 } 3266 try { 3267 mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq); 3268 } catch (RemoteException e) { 3269 Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e); 3270 } 3271 } 3272 } 3273 3274 @Override onTimeShiftCurrentPositionChanged(long timeMs)3275 public void onTimeShiftCurrentPositionChanged(long timeMs) { 3276 synchronized (mLock) { 3277 if (DEBUG) { 3278 Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")"); 3279 } 3280 if (mSessionState.session == null || mSessionState.client == null) { 3281 return; 3282 } 3283 try { 3284 mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs, 3285 mSessionState.seq); 3286 } catch (RemoteException e) { 3287 Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e); 3288 } 3289 } 3290 } 3291 3292 // For the recording session only 3293 @Override onTuned(Uri channelUri)3294 public void onTuned(Uri channelUri) { 3295 synchronized (mLock) { 3296 if (DEBUG) { 3297 Slog.d(TAG, "onTuned()"); 3298 } 3299 if (mSessionState.session == null || mSessionState.client == null) { 3300 return; 3301 } 3302 try { 3303 mSessionState.client.onTuned(mSessionState.seq, channelUri); 3304 } catch (RemoteException e) { 3305 Slog.e(TAG, "error in onTuned", e); 3306 } 3307 } 3308 } 3309 3310 // For the recording session only 3311 @Override onRecordingStopped(Uri recordedProgramUri)3312 public void onRecordingStopped(Uri recordedProgramUri) { 3313 synchronized (mLock) { 3314 if (DEBUG) { 3315 Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri 3316 + ")"); 3317 } 3318 if (mSessionState.session == null || mSessionState.client == null) { 3319 return; 3320 } 3321 try { 3322 mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq); 3323 } catch (RemoteException e) { 3324 Slog.e(TAG, "error in onRecordingStopped", e); 3325 } 3326 } 3327 } 3328 3329 // For the recording session only 3330 @Override onError(int error)3331 public void onError(int error) { 3332 synchronized (mLock) { 3333 if (DEBUG) { 3334 Slog.d(TAG, "onError(error=" + error + ")"); 3335 } 3336 if (mSessionState.session == null || mSessionState.client == null) { 3337 return; 3338 } 3339 try { 3340 mSessionState.client.onError(error, mSessionState.seq); 3341 } catch (RemoteException e) { 3342 Slog.e(TAG, "error in onError", e); 3343 } 3344 } 3345 } 3346 } 3347 3348 @VisibleForTesting getVideoUnavailableReasonForStatsd( @vInputManager.VideoUnavailableReason int reason)3349 static int getVideoUnavailableReasonForStatsd( 3350 @TvInputManager.VideoUnavailableReason int reason) { 3351 int loggedReason = reason + FrameworkStatsLog 3352 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; 3353 if (loggedReason < FrameworkStatsLog 3354 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN 3355 || loggedReason > FrameworkStatsLog 3356 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) { 3357 loggedReason = FrameworkStatsLog 3358 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; 3359 } 3360 return loggedReason; 3361 } 3362 getUserStateLocked(int userId)3363 private UserState getUserStateLocked(int userId) { 3364 return mUserStates.get(userId); 3365 } 3366 3367 private static final class WatchLogHandler extends Handler { 3368 // There are only two kinds of watch events that can happen on the system: 3369 // 1. The current TV input session is tuned to a new channel. 3370 // 2. The session is released for some reason. 3371 // The former indicates the end of the previous log entry, if any, followed by the start of 3372 // a new entry. The latter indicates the end of the most recent entry for the given session. 3373 // Here the system supplies the database the smallest set of information only that is 3374 // sufficient to consolidate the log entries while minimizing database operations in the 3375 // system service. 3376 static final int MSG_LOG_WATCH_START = 1; 3377 static final int MSG_LOG_WATCH_END = 2; 3378 static final int MSG_SWITCH_CONTENT_RESOLVER = 3; 3379 3380 private ContentResolver mContentResolver; 3381 WatchLogHandler(ContentResolver contentResolver, Looper looper)3382 WatchLogHandler(ContentResolver contentResolver, Looper looper) { 3383 super(looper); 3384 mContentResolver = contentResolver; 3385 } 3386 3387 @Override handleMessage(Message msg)3388 public void handleMessage(Message msg) { 3389 switch (msg.what) { 3390 case MSG_LOG_WATCH_START: { 3391 SomeArgs args = (SomeArgs) msg.obj; 3392 String packageName = (String) args.arg1; 3393 long watchStartTime = (long) args.arg2; 3394 long channelId = (long) args.arg3; 3395 Bundle tuneParams = (Bundle) args.arg4; 3396 IBinder sessionToken = (IBinder) args.arg5; 3397 3398 ContentValues values = new ContentValues(); 3399 values.put(TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME, packageName); 3400 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 3401 watchStartTime); 3402 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 3403 if (tuneParams != null) { 3404 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS, 3405 encodeTuneParams(tuneParams)); 3406 } 3407 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN, 3408 sessionToken.toString()); 3409 3410 try{ 3411 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 3412 }catch(IllegalArgumentException ex){ 3413 Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_START", ex); 3414 } 3415 args.recycle(); 3416 break; 3417 } 3418 case MSG_LOG_WATCH_END: { 3419 SomeArgs args = (SomeArgs) msg.obj; 3420 IBinder sessionToken = (IBinder) args.arg1; 3421 long watchEndTime = (long) args.arg2; 3422 3423 ContentValues values = new ContentValues(); 3424 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 3425 watchEndTime); 3426 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN, 3427 sessionToken.toString()); 3428 3429 try{ 3430 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 3431 }catch(IllegalArgumentException ex){ 3432 Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_END", ex); 3433 } 3434 args.recycle(); 3435 break; 3436 } 3437 case MSG_SWITCH_CONTENT_RESOLVER: { 3438 mContentResolver = (ContentResolver) msg.obj; 3439 break; 3440 } 3441 default: { 3442 Slog.w(TAG, "unhandled message code: " + msg.what); 3443 break; 3444 } 3445 } 3446 } 3447 encodeTuneParams(Bundle tuneParams)3448 private String encodeTuneParams(Bundle tuneParams) { 3449 StringBuilder builder = new StringBuilder(); 3450 Set<String> keySet = tuneParams.keySet(); 3451 Iterator<String> it = keySet.iterator(); 3452 while (it.hasNext()) { 3453 String key = it.next(); 3454 Object value = tuneParams.get(key); 3455 if (value == null) { 3456 continue; 3457 } 3458 builder.append(replaceEscapeCharacters(key)); 3459 builder.append("="); 3460 builder.append(replaceEscapeCharacters(value.toString())); 3461 if (it.hasNext()) { 3462 builder.append(", "); 3463 } 3464 } 3465 return builder.toString(); 3466 } 3467 replaceEscapeCharacters(String src)3468 private String replaceEscapeCharacters(String src) { 3469 final char ESCAPE_CHARACTER = '%'; 3470 final String ENCODING_TARGET_CHARACTERS = "%=,"; 3471 StringBuilder builder = new StringBuilder(); 3472 for (char ch : src.toCharArray()) { 3473 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) { 3474 builder.append(ESCAPE_CHARACTER); 3475 } 3476 builder.append(ch); 3477 } 3478 return builder.toString(); 3479 } 3480 } 3481 3482 private final class HardwareListener implements TvInputHardwareManager.Listener { 3483 @Override onStateChanged(String inputId, int state)3484 public void onStateChanged(String inputId, int state) { 3485 synchronized (mLock) { 3486 setStateLocked(inputId, state, mCurrentUserId); 3487 } 3488 } 3489 3490 @Override onHardwareDeviceAdded(TvInputHardwareInfo info)3491 public void onHardwareDeviceAdded(TvInputHardwareInfo info) { 3492 synchronized (mLock) { 3493 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 3494 // Broadcast the event to all hardware inputs. 3495 for (ServiceState serviceState : userState.serviceStateMap.values()) { 3496 if (!serviceState.isHardware || serviceState.service == null) continue; 3497 try { 3498 serviceState.service.notifyHardwareAdded(info); 3499 } catch (RemoteException e) { 3500 Slog.e(TAG, "error in notifyHardwareAdded", e); 3501 } 3502 } 3503 } 3504 } 3505 3506 @Override onHardwareDeviceRemoved(TvInputHardwareInfo info)3507 public void onHardwareDeviceRemoved(TvInputHardwareInfo info) { 3508 synchronized (mLock) { 3509 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 3510 // Broadcast the event to all hardware inputs. 3511 for (ServiceState serviceState : userState.serviceStateMap.values()) { 3512 if (!serviceState.isHardware || serviceState.service == null) continue; 3513 try { 3514 serviceState.service.notifyHardwareRemoved(info); 3515 } catch (RemoteException e) { 3516 Slog.e(TAG, "error in notifyHardwareRemoved", e); 3517 } 3518 } 3519 } 3520 } 3521 3522 @Override onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo)3523 public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { 3524 synchronized (mLock) { 3525 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 3526 // Broadcast the event to all hardware inputs. 3527 for (ServiceState serviceState : userState.serviceStateMap.values()) { 3528 if (!serviceState.isHardware || serviceState.service == null) continue; 3529 try { 3530 serviceState.service.notifyHdmiDeviceAdded(deviceInfo); 3531 } catch (RemoteException e) { 3532 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); 3533 } 3534 } 3535 } 3536 } 3537 3538 @Override onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo)3539 public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { 3540 synchronized (mLock) { 3541 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 3542 // Broadcast the event to all hardware inputs. 3543 for (ServiceState serviceState : userState.serviceStateMap.values()) { 3544 if (!serviceState.isHardware || serviceState.service == null) continue; 3545 try { 3546 serviceState.service.notifyHdmiDeviceRemoved(deviceInfo); 3547 } catch (RemoteException e) { 3548 Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e); 3549 } 3550 } 3551 } 3552 } 3553 3554 @Override onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo)3555 public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) { 3556 synchronized (mLock) { 3557 Integer state; 3558 switch (deviceInfo.getDevicePowerStatus()) { 3559 case HdmiControlManager.POWER_STATUS_ON: 3560 state = INPUT_STATE_CONNECTED; 3561 break; 3562 case HdmiControlManager.POWER_STATUS_STANDBY: 3563 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON: 3564 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY: 3565 state = INPUT_STATE_CONNECTED_STANDBY; 3566 break; 3567 case HdmiControlManager.POWER_STATUS_UNKNOWN: 3568 default: 3569 state = null; 3570 break; 3571 } 3572 if (state != null) { 3573 setStateLocked(inputId, state, mCurrentUserId); 3574 } 3575 UserState userState = getOrCreateUserStateLocked(mCurrentUserId); 3576 // Broadcast the event to all hardware inputs. 3577 for (ServiceState serviceState : userState.serviceStateMap.values()) { 3578 if (!serviceState.isHardware || serviceState.service == null) continue; 3579 try { 3580 serviceState.service.notifyHdmiDeviceUpdated(deviceInfo); 3581 } catch (RemoteException e) { 3582 Slog.e(TAG, "error in notifyHdmiDeviceUpdated", e); 3583 } 3584 } 3585 } 3586 } 3587 } 3588 3589 private static class SessionNotFoundException extends IllegalArgumentException { SessionNotFoundException(String name)3590 public SessionNotFoundException(String name) { 3591 super(name); 3592 } 3593 } 3594 3595 private static class ClientPidNotFoundException extends IllegalArgumentException { ClientPidNotFoundException(String name)3596 public ClientPidNotFoundException(String name) { 3597 super(name); 3598 } 3599 } 3600 } 3601