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