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