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