TvInputManagerService.java revision 1a6b25eabcc1fb66e6e8d76f91fd413e18b793a9
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 17package com.android.server.tv; 18 19import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; 20import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED; 21 22import android.app.ActivityManager; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.ContentProviderOperation; 26import android.content.ContentProviderResult; 27import android.content.ContentResolver; 28import android.content.ContentUris; 29import android.content.ContentValues; 30import android.content.Context; 31import android.content.Intent; 32import android.content.IntentFilter; 33import android.content.OperationApplicationException; 34import android.content.ServiceConnection; 35import android.content.pm.PackageManager; 36import android.content.pm.ResolveInfo; 37import android.content.pm.ServiceInfo; 38import android.database.Cursor; 39import android.graphics.Rect; 40import android.hardware.hdmi.HdmiCecDeviceInfo; 41import android.media.tv.ITvInputClient; 42import android.media.tv.ITvInputHardware; 43import android.media.tv.ITvInputHardwareCallback; 44import android.media.tv.ITvInputManager; 45import android.media.tv.ITvInputManagerCallback; 46import android.media.tv.ITvInputService; 47import android.media.tv.ITvInputServiceCallback; 48import android.media.tv.ITvInputSession; 49import android.media.tv.ITvInputSessionCallback; 50import android.media.tv.TvContentRating; 51import android.media.tv.TvContract; 52import android.media.tv.TvInputHardwareInfo; 53import android.media.tv.TvInputInfo; 54import android.media.tv.TvInputManager; 55import android.media.tv.TvInputService; 56import android.media.tv.TvStreamConfig; 57import android.media.tv.TvTrackInfo; 58import android.net.Uri; 59import android.os.Binder; 60import android.os.Bundle; 61import android.os.Handler; 62import android.os.IBinder; 63import android.os.Looper; 64import android.os.Message; 65import android.os.Process; 66import android.os.RemoteException; 67import android.os.UserHandle; 68import android.util.Slog; 69import android.util.SparseArray; 70import android.view.InputChannel; 71import android.view.Surface; 72 73import com.android.internal.content.PackageMonitor; 74import com.android.internal.os.SomeArgs; 75import com.android.internal.util.IndentingPrintWriter; 76import com.android.server.IoThread; 77import com.android.server.SystemService; 78 79import org.xmlpull.v1.XmlPullParserException; 80 81import java.io.FileDescriptor; 82import java.io.IOException; 83import java.io.PrintWriter; 84import java.util.ArrayList; 85import java.util.HashMap; 86import java.util.HashSet; 87import java.util.Iterator; 88import java.util.List; 89import java.util.Map; 90import java.util.Set; 91 92/** This class provides a system service that manages television inputs. */ 93public final class TvInputManagerService extends SystemService { 94 // STOPSHIP: Turn debugging off. 95 private static final boolean DEBUG = true; 96 private static final String TAG = "TvInputManagerService"; 97 98 private final Context mContext; 99 private final TvInputHardwareManager mTvInputHardwareManager; 100 101 private final ContentResolver mContentResolver; 102 103 // A global lock. 104 private final Object mLock = new Object(); 105 106 // ID of the current user. 107 private int mCurrentUserId = UserHandle.USER_OWNER; 108 109 // A map from user id to UserState. 110 private final SparseArray<UserState> mUserStates = new SparseArray<UserState>(); 111 112 private final Handler mLogHandler; 113 114 public TvInputManagerService(Context context) { 115 super(context); 116 117 mContext = context; 118 mContentResolver = context.getContentResolver(); 119 mLogHandler = new LogHandler(IoThread.get().getLooper()); 120 121 mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener()); 122 123 synchronized (mLock) { 124 mUserStates.put(mCurrentUserId, new UserState(mContext, mCurrentUserId)); 125 } 126 } 127 128 @Override 129 public void onStart() { 130 publishBinderService(Context.TV_INPUT_SERVICE, new BinderService()); 131 } 132 133 @Override 134 public void onBootPhase(int phase) { 135 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 136 registerBroadcastReceivers(); 137 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 138 synchronized (mLock) { 139 buildTvInputListLocked(mCurrentUserId); 140 } 141 } 142 mTvInputHardwareManager.onBootPhase(phase); 143 } 144 145 private void registerBroadcastReceivers() { 146 PackageMonitor monitor = new PackageMonitor() { 147 @Override 148 public void onSomePackagesChanged() { 149 synchronized (mLock) { 150 buildTvInputListLocked(mCurrentUserId); 151 } 152 } 153 154 @Override 155 public void onPackageRemoved(String packageName, int uid) { 156 synchronized (mLock) { 157 UserState userState = getUserStateLocked(mCurrentUserId); 158 if (!userState.packageSet.contains(packageName)) { 159 // Not a TV input package. 160 return; 161 } 162 } 163 164 ArrayList<ContentProviderOperation> operations = 165 new ArrayList<ContentProviderOperation>(); 166 167 String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?"; 168 String[] selectionArgs = { packageName }; 169 170 operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI) 171 .withSelection(selection, selectionArgs).build()); 172 operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI) 173 .withSelection(selection, selectionArgs).build()); 174 operations.add(ContentProviderOperation 175 .newDelete(TvContract.WatchedPrograms.CONTENT_URI) 176 .withSelection(selection, selectionArgs).build()); 177 178 ContentProviderResult[] results = null; 179 try { 180 results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations); 181 } catch (RemoteException | OperationApplicationException e) { 182 Slog.e(TAG, "error in applyBatch" + e); 183 } 184 185 if (DEBUG) { 186 Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid 187 + ")"); 188 Slog.d(TAG, "results=" + results); 189 } 190 } 191 }; 192 monitor.register(mContext, null, UserHandle.ALL, true); 193 194 IntentFilter intentFilter = new IntentFilter(); 195 intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 196 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 197 mContext.registerReceiverAsUser(new BroadcastReceiver() { 198 @Override 199 public void onReceive(Context context, Intent intent) { 200 String action = intent.getAction(); 201 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 202 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 203 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 204 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 205 } 206 } 207 }, UserHandle.ALL, intentFilter, null, null); 208 } 209 210 private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) { 211 return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, 212 component.getPackageName()) == PackageManager.PERMISSION_GRANTED; 213 } 214 215 private void buildTvInputListLocked(int userId) { 216 UserState userState = getUserStateLocked(userId); 217 218 Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>(); 219 userState.packageSet.clear(); 220 221 if (DEBUG) Slog.d(TAG, "buildTvInputList"); 222 PackageManager pm = mContext.getPackageManager(); 223 List<ResolveInfo> services = pm.queryIntentServices( 224 new Intent(TvInputService.SERVICE_INTERFACE), 225 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); 226 List<TvInputInfo> inputList = new ArrayList<TvInputInfo>(); 227 for (ResolveInfo ri : services) { 228 ServiceInfo si = ri.serviceInfo; 229 if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { 230 Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission " 231 + android.Manifest.permission.BIND_TV_INPUT); 232 continue; 233 } 234 try { 235 inputList.clear(); 236 ComponentName component = new ComponentName(si.packageName, si.name); 237 if (hasHardwarePermission(pm, component)) { 238 ServiceState serviceState = userState.serviceStateMap.get(component); 239 if (serviceState == null) { 240 // We see this hardware TV input service for the first time; we need to 241 // prepare the ServiceState object so that we can connect to the service and 242 // let it add TvInputInfo objects to mInputList if there's any. 243 serviceState = new ServiceState(component, userId); 244 userState.serviceStateMap.put(component, serviceState); 245 } else { 246 inputList.addAll(serviceState.mInputList); 247 } 248 } else { 249 inputList.add(TvInputInfo.createTvInputInfo(mContext, ri)); 250 } 251 252 for (TvInputInfo info : inputList) { 253 if (DEBUG) Slog.d(TAG, "add " + info.getId()); 254 TvInputState state = userState.inputMap.get(info.getId()); 255 if (state == null) { 256 state = new TvInputState(); 257 } 258 state.mInfo = info; 259 inputMap.put(info.getId(), state); 260 } 261 262 // Reconnect the service if existing input is updated. 263 updateServiceConnectionLocked(component, userId); 264 265 userState.packageSet.add(si.packageName); 266 } catch (IOException | XmlPullParserException e) { 267 Slog.e(TAG, "Can't load TV input " + si.name, e); 268 } 269 } 270 271 for (String inputId : inputMap.keySet()) { 272 if (!userState.inputMap.containsKey(inputId)) { 273 notifyInputAddedLocked(userState, inputId); 274 } 275 } 276 277 for (String inputId : userState.inputMap.keySet()) { 278 if (!inputMap.containsKey(inputId)) { 279 notifyInputRemovedLocked(userState, inputId); 280 } 281 } 282 283 userState.inputMap.clear(); 284 userState.inputMap = inputMap; 285 } 286 287 private void switchUser(int userId) { 288 synchronized (mLock) { 289 if (mCurrentUserId == userId) { 290 return; 291 } 292 // final int oldUserId = mCurrentUserId; 293 // TODO: Release services and sessions in the old user state, if needed. 294 mCurrentUserId = userId; 295 296 UserState userState = mUserStates.get(userId); 297 if (userState == null) { 298 userState = new UserState(mContext, userId); 299 } 300 mUserStates.put(userId, userState); 301 buildTvInputListLocked(userId); 302 } 303 } 304 305 private void removeUser(int userId) { 306 synchronized (mLock) { 307 UserState userState = mUserStates.get(userId); 308 if (userState == null) { 309 return; 310 } 311 // Release created sessions. 312 for (SessionState state : userState.sessionStateMap.values()) { 313 if (state.mSession != null) { 314 try { 315 state.mSession.release(); 316 } catch (RemoteException e) { 317 Slog.e(TAG, "error in release", e); 318 } 319 } 320 } 321 userState.sessionStateMap.clear(); 322 323 // Unregister all callbacks and unbind all services. 324 for (ServiceState serviceState : userState.serviceStateMap.values()) { 325 if (serviceState.mCallback != null) { 326 try { 327 serviceState.mService.unregisterCallback(serviceState.mCallback); 328 } catch (RemoteException e) { 329 Slog.e(TAG, "error in unregisterCallback", e); 330 } 331 } 332 serviceState.mClientTokens.clear(); 333 mContext.unbindService(serviceState.mConnection); 334 } 335 userState.serviceStateMap.clear(); 336 337 userState.clientStateMap.clear(); 338 339 mUserStates.remove(userId); 340 } 341 } 342 343 private UserState getUserStateLocked(int userId) { 344 UserState userState = mUserStates.get(userId); 345 if (userState == null) { 346 throw new IllegalStateException("User state not found for user ID " + userId); 347 } 348 return userState; 349 } 350 351 private ServiceState getServiceStateLocked(ComponentName component, int userId) { 352 UserState userState = getUserStateLocked(userId); 353 ServiceState serviceState = userState.serviceStateMap.get(component); 354 if (serviceState == null) { 355 throw new IllegalStateException("Service state not found for " + component + " (userId=" 356 + userId + ")"); 357 } 358 return serviceState; 359 } 360 361 private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) { 362 UserState userState = getUserStateLocked(userId); 363 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 364 if (sessionState == null) { 365 throw new IllegalArgumentException("Session state not found for token " + sessionToken); 366 } 367 // Only the application that requested this session or the system can access it. 368 if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) { 369 throw new SecurityException("Illegal access to the session with token " + sessionToken 370 + " from uid " + callingUid); 371 } 372 return sessionState; 373 } 374 375 private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) { 376 return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId)); 377 } 378 379 private ITvInputSession getSessionLocked(SessionState sessionState) { 380 ITvInputSession session = sessionState.mSession; 381 if (session == null) { 382 throw new IllegalStateException("Session not yet created for token " 383 + sessionState.mSessionToken); 384 } 385 return session; 386 } 387 388 private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId, 389 String methodName) { 390 return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false, 391 false, methodName, null); 392 } 393 394 private static boolean shouldMaintainConnection(ServiceState serviceState) { 395 return !serviceState.mClientTokens.isEmpty() 396 || !serviceState.mSessionTokens.isEmpty() 397 || serviceState.mIsHardware; 398 // TODO: Find a way to maintain connection only when necessary. 399 } 400 401 private void updateServiceConnectionLocked(ComponentName component, int userId) { 402 UserState userState = getUserStateLocked(userId); 403 ServiceState serviceState = userState.serviceStateMap.get(component); 404 if (serviceState == null) { 405 return; 406 } 407 if (serviceState.mReconnecting) { 408 if (!serviceState.mSessionTokens.isEmpty()) { 409 // wait until all the sessions are removed. 410 return; 411 } 412 serviceState.mReconnecting = false; 413 } 414 boolean maintainConnection = shouldMaintainConnection(serviceState); 415 if (serviceState.mService == null && maintainConnection && userId == mCurrentUserId) { 416 // This means that the service is not yet connected but its state indicates that we 417 // have pending requests. Then, connect the service. 418 if (serviceState.mBound) { 419 // We have already bound to the service so we don't try to bind again until after we 420 // unbind later on. 421 return; 422 } 423 if (DEBUG) { 424 Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")"); 425 } 426 427 Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component); 428 // Binding service may fail if the service is updating. 429 // In that case, the connection will be revived in buildTvInputListLocked called by 430 // onSomePackagesChanged. 431 serviceState.mBound = mContext.bindServiceAsUser( 432 i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId)); 433 } else if (serviceState.mService != null && !maintainConnection) { 434 // This means that the service is already connected but its state indicates that we have 435 // nothing to do with it. Then, disconnect the service. 436 if (DEBUG) { 437 Slog.d(TAG, "unbindService(service=" + component + ")"); 438 } 439 mContext.unbindService(serviceState.mConnection); 440 userState.serviceStateMap.remove(component); 441 } 442 } 443 444 private ClientState createClientStateLocked(IBinder clientToken, int userId) { 445 UserState userState = getUserStateLocked(userId); 446 ClientState clientState = new ClientState(clientToken, userId); 447 try { 448 clientToken.linkToDeath(clientState, 0); 449 } catch (RemoteException e) { 450 Slog.e(TAG, "Client is already died."); 451 } 452 userState.clientStateMap.put(clientToken, clientState); 453 return clientState; 454 } 455 456 private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken, 457 final int userId) { 458 final UserState userState = getUserStateLocked(userId); 459 final SessionState sessionState = userState.sessionStateMap.get(sessionToken); 460 if (DEBUG) { 461 Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInfo.getId() + ")"); 462 } 463 464 final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString()); 465 466 // Set up a callback to send the session token. 467 ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() { 468 @Override 469 public void onSessionCreated(ITvInputSession session) { 470 if (DEBUG) { 471 Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")"); 472 } 473 synchronized (mLock) { 474 sessionState.mSession = session; 475 if (session == null) { 476 removeSessionStateLocked(sessionToken, userId); 477 sendSessionTokenToClientLocked(sessionState.mClient, 478 sessionState.mInfo.getId(), null, null, sessionState.mSeq); 479 } else { 480 try { 481 session.asBinder().linkToDeath(sessionState, 0); 482 } catch (RemoteException e) { 483 Slog.e(TAG, "Session is already died."); 484 } 485 486 IBinder clientToken = sessionState.mClient.asBinder(); 487 ClientState clientState = userState.clientStateMap.get(clientToken); 488 if (clientState == null) { 489 clientState = createClientStateLocked(clientToken, userId); 490 } 491 clientState.mSessionTokens.add(sessionState.mSessionToken); 492 493 sendSessionTokenToClientLocked(sessionState.mClient, 494 sessionState.mInfo.getId(), sessionToken, channels[0], 495 sessionState.mSeq); 496 } 497 channels[0].dispose(); 498 } 499 } 500 501 @Override 502 public void onChannelRetuned(Uri channelUri) { 503 synchronized (mLock) { 504 if (DEBUG) { 505 Slog.d(TAG, "onChannelRetuned(" + channelUri + ")"); 506 } 507 if (sessionState.mSession == null || sessionState.mClient == null) { 508 return; 509 } 510 try { 511 // TODO: Consider adding this channel change in the watch log. When we do 512 // that, how we can protect the watch log from malicious tv inputs should 513 // be addressed. e.g. add a field which represents where the channel change 514 // originated from. 515 sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq); 516 } catch (RemoteException e) { 517 Slog.e(TAG, "error in onChannelRetuned"); 518 } 519 } 520 } 521 522 @Override 523 public void onTrackInfoChanged(List<TvTrackInfo> tracks) { 524 synchronized (mLock) { 525 if (DEBUG) { 526 Slog.d(TAG, "onTrackInfoChanged(" + tracks + ")"); 527 } 528 if (sessionState.mSession == null || sessionState.mClient == null) { 529 return; 530 } 531 try { 532 sessionState.mClient.onTrackInfoChanged(tracks, 533 sessionState.mSeq); 534 } catch (RemoteException e) { 535 Slog.e(TAG, "error in onTrackInfoChanged"); 536 } 537 } 538 } 539 540 @Override 541 public void onTrackSelectionChanged(List<TvTrackInfo> selectedTracks) { 542 synchronized (mLock) { 543 if (DEBUG) { 544 Slog.d(TAG, "onTrackSelectionChanged(" + selectedTracks + ")"); 545 } 546 if (sessionState.mSession == null || sessionState.mClient == null) { 547 return; 548 } 549 try { 550 sessionState.mClient.onTrackSelectionChanged(selectedTracks, 551 sessionState.mSeq); 552 } catch (RemoteException e) { 553 Slog.e(TAG, "error in onTrackSelectionChanged"); 554 } 555 } 556 } 557 558 @Override 559 public void onVideoAvailable() { 560 synchronized (mLock) { 561 if (DEBUG) { 562 Slog.d(TAG, "onVideoAvailable()"); 563 } 564 if (sessionState.mSession == null || sessionState.mClient == null) { 565 return; 566 } 567 try { 568 sessionState.mClient.onVideoAvailable(sessionState.mSeq); 569 } catch (RemoteException e) { 570 Slog.e(TAG, "error in onVideoAvailable"); 571 } 572 } 573 } 574 575 @Override 576 public void onVideoUnavailable(int reason) { 577 synchronized (mLock) { 578 if (DEBUG) { 579 Slog.d(TAG, "onVideoUnavailable(" + reason + ")"); 580 } 581 if (sessionState.mSession == null || sessionState.mClient == null) { 582 return; 583 } 584 try { 585 sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq); 586 } catch (RemoteException e) { 587 Slog.e(TAG, "error in onVideoUnavailable"); 588 } 589 } 590 } 591 592 @Override 593 public void onContentAllowed() { 594 synchronized (mLock) { 595 if (DEBUG) { 596 Slog.d(TAG, "onContentAllowed()"); 597 } 598 if (sessionState.mSession == null || sessionState.mClient == null) { 599 return; 600 } 601 try { 602 sessionState.mClient.onContentAllowed(sessionState.mSeq); 603 } catch (RemoteException e) { 604 Slog.e(TAG, "error in onContentAllowed"); 605 } 606 } 607 } 608 609 @Override 610 public void onContentBlocked(String rating) { 611 synchronized (mLock) { 612 if (DEBUG) { 613 Slog.d(TAG, "onContentBlocked()"); 614 } 615 if (sessionState.mSession == null || sessionState.mClient == null) { 616 return; 617 } 618 try { 619 sessionState.mClient.onContentBlocked(rating, sessionState.mSeq); 620 } catch (RemoteException e) { 621 Slog.e(TAG, "error in onContentBlocked"); 622 } 623 } 624 } 625 626 @Override 627 public void onSessionEvent(String eventType, Bundle eventArgs) { 628 synchronized (mLock) { 629 if (DEBUG) { 630 Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")"); 631 } 632 if (sessionState.mSession == null || sessionState.mClient == null) { 633 return; 634 } 635 try { 636 sessionState.mClient.onSessionEvent(eventType, eventArgs, 637 sessionState.mSeq); 638 } catch (RemoteException e) { 639 Slog.e(TAG, "error in onSessionEvent"); 640 } 641 } 642 } 643 }; 644 645 // Create a session. When failed, send a null token immediately. 646 try { 647 service.createSession(channels[1], callback, sessionState.mInfo.getId()); 648 } catch (RemoteException e) { 649 Slog.e(TAG, "error in createSession", e); 650 removeSessionStateLocked(sessionToken, userId); 651 sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInfo.getId(), null, 652 null, sessionState.mSeq); 653 } 654 channels[1].dispose(); 655 } 656 657 private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId, 658 IBinder sessionToken, InputChannel channel, int seq) { 659 try { 660 client.onSessionCreated(inputId, sessionToken, channel, seq); 661 } catch (RemoteException exception) { 662 Slog.e(TAG, "error in onSessionCreated", exception); 663 } 664 } 665 666 private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { 667 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 668 if (sessionState.mSession != null) { 669 try { 670 sessionState.mSession.release(); 671 } catch (RemoteException e) { 672 Slog.w(TAG, "session is already disapeared", e); 673 } 674 sessionState.mSession = null; 675 } 676 removeSessionStateLocked(sessionToken, userId); 677 } 678 679 private void removeSessionStateLocked(IBinder sessionToken, int userId) { 680 UserState userState = getUserStateLocked(userId); 681 if (sessionToken == userState.mainSessionToken) { 682 userState.mainSessionToken = null; 683 } 684 685 // Remove the session state from the global session state map of the current user. 686 SessionState sessionState = userState.sessionStateMap.remove(sessionToken); 687 688 // Close the open log entry, if any. 689 if (sessionState.mLogUri != null) { 690 SomeArgs args = SomeArgs.obtain(); 691 args.arg1 = sessionState.mLogUri; 692 args.arg2 = System.currentTimeMillis(); 693 mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget(); 694 } 695 696 // Also remove the session token from the session token list of the current client and 697 // service. 698 ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder()); 699 if (clientState != null) { 700 clientState.mSessionTokens.remove(sessionToken); 701 if (clientState.isEmpty()) { 702 userState.clientStateMap.remove(sessionState.mClient.asBinder()); 703 } 704 } 705 706 TvInputInfo info = sessionState.mInfo; 707 if (info != null) { 708 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 709 if (serviceState != null) { 710 serviceState.mSessionTokens.remove(sessionToken); 711 } 712 } 713 updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId); 714 } 715 716 private void notifyInputAddedLocked(UserState userState, String inputId) { 717 if (DEBUG) { 718 Slog.d(TAG, "notifyInputAdded: inputId = " + inputId); 719 } 720 for (ITvInputManagerCallback callback : userState.callbackSet) { 721 try { 722 callback.onInputAdded(inputId); 723 } catch (RemoteException e) { 724 Slog.e(TAG, "Failed to report added input to callback."); 725 } 726 } 727 } 728 729 private void notifyInputRemovedLocked(UserState userState, String inputId) { 730 if (DEBUG) { 731 Slog.d(TAG, "notifyInputRemovedLocked: inputId = " + inputId); 732 } 733 for (ITvInputManagerCallback callback : userState.callbackSet) { 734 try { 735 callback.onInputRemoved(inputId); 736 } catch (RemoteException e) { 737 Slog.e(TAG, "Failed to report removed input to callback."); 738 } 739 } 740 } 741 742 private void notifyInputStateChangedLocked(UserState userState, String inputId, 743 int state, ITvInputManagerCallback targetCallback) { 744 if (DEBUG) { 745 Slog.d(TAG, "notifyInputStateChangedLocked: inputId = " + inputId 746 + "; state = " + state); 747 } 748 if (targetCallback == null) { 749 for (ITvInputManagerCallback callback : userState.callbackSet) { 750 try { 751 callback.onInputStateChanged(inputId, state); 752 } catch (RemoteException e) { 753 Slog.e(TAG, "Failed to report state change to callback."); 754 } 755 } 756 } else { 757 try { 758 targetCallback.onInputStateChanged(inputId, state); 759 } catch (RemoteException e) { 760 Slog.e(TAG, "Failed to report state change to callback."); 761 } 762 } 763 } 764 765 private void setStateLocked(String inputId, int state, int userId) { 766 UserState userState = getUserStateLocked(userId); 767 TvInputState inputState = userState.inputMap.get(inputId); 768 ServiceState serviceState = userState.serviceStateMap.get(inputState.mInfo.getComponent()); 769 int oldState = inputState.mState; 770 inputState.mState = state; 771 if (serviceState != null && serviceState.mService == null 772 && shouldMaintainConnection(serviceState)) { 773 // We don't notify state change while reconnecting. It should remain disconnected. 774 return; 775 } 776 if (oldState != state) { 777 notifyInputStateChangedLocked(userState, inputId, state, null); 778 } 779 } 780 781 private final class BinderService extends ITvInputManager.Stub { 782 @Override 783 public List<TvInputInfo> getTvInputList(int userId) { 784 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 785 Binder.getCallingUid(), userId, "getTvInputList"); 786 final long identity = Binder.clearCallingIdentity(); 787 try { 788 synchronized (mLock) { 789 UserState userState = getUserStateLocked(resolvedUserId); 790 List<TvInputInfo> inputList = new ArrayList<TvInputInfo>(); 791 for (TvInputState state : userState.inputMap.values()) { 792 inputList.add(state.mInfo); 793 } 794 return inputList; 795 } 796 } finally { 797 Binder.restoreCallingIdentity(identity); 798 } 799 } 800 801 @Override 802 public TvInputInfo getTvInputInfo(String inputId, int userId) { 803 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 804 Binder.getCallingUid(), userId, "getTvInputInfo"); 805 final long identity = Binder.clearCallingIdentity(); 806 try { 807 synchronized (mLock) { 808 UserState userState = getUserStateLocked(resolvedUserId); 809 TvInputState state = userState.inputMap.get(inputId); 810 return state == null ? null : state.mInfo; 811 } 812 } finally { 813 Binder.restoreCallingIdentity(identity); 814 } 815 } 816 817 @Override 818 public void registerCallback(final ITvInputManagerCallback callback, int userId) { 819 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 820 Binder.getCallingUid(), userId, "registerCallback"); 821 final long identity = Binder.clearCallingIdentity(); 822 try { 823 synchronized (mLock) { 824 UserState userState = getUserStateLocked(resolvedUserId); 825 userState.callbackSet.add(callback); 826 for (TvInputState state : userState.inputMap.values()) { 827 notifyInputStateChangedLocked(userState, state.mInfo.getId(), 828 state.mState, callback); 829 } 830 } 831 } finally { 832 Binder.restoreCallingIdentity(identity); 833 } 834 } 835 836 @Override 837 public void unregisterCallback(ITvInputManagerCallback callback, int userId) { 838 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 839 Binder.getCallingUid(), userId, "unregisterCallback"); 840 final long identity = Binder.clearCallingIdentity(); 841 try { 842 synchronized (mLock) { 843 UserState userState = getUserStateLocked(resolvedUserId); 844 userState.callbackSet.remove(callback); 845 } 846 } finally { 847 Binder.restoreCallingIdentity(identity); 848 } 849 } 850 851 @Override 852 public boolean isParentalControlsEnabled(int userId) { 853 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 854 Binder.getCallingUid(), userId, "isParentalControlsEnabled"); 855 final long identity = Binder.clearCallingIdentity(); 856 try { 857 synchronized (mLock) { 858 UserState userState = getUserStateLocked(resolvedUserId); 859 return userState.persistentDataStore.isParentalControlsEnabled(); 860 } 861 } finally { 862 Binder.restoreCallingIdentity(identity); 863 } 864 } 865 866 @Override 867 public void setParentalControlsEnabled(boolean enabled, int userId) { 868 ensureParentalControlsPermission(); 869 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 870 Binder.getCallingUid(), userId, "setParentalControlsEnabled"); 871 final long identity = Binder.clearCallingIdentity(); 872 try { 873 synchronized (mLock) { 874 UserState userState = getUserStateLocked(resolvedUserId); 875 userState.persistentDataStore.setParentalControlsEnabled(enabled); 876 } 877 } finally { 878 Binder.restoreCallingIdentity(identity); 879 } 880 } 881 882 @Override 883 public boolean isRatingBlocked(String rating, int userId) { 884 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 885 Binder.getCallingUid(), userId, "isRatingBlocked"); 886 final long identity = Binder.clearCallingIdentity(); 887 try { 888 synchronized (mLock) { 889 UserState userState = getUserStateLocked(resolvedUserId); 890 return userState.persistentDataStore.isRatingBlocked( 891 TvContentRating.unflattenFromString(rating)); 892 } 893 } finally { 894 Binder.restoreCallingIdentity(identity); 895 } 896 } 897 898 @Override 899 public List<String> getBlockedRatings(int userId) { 900 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 901 Binder.getCallingUid(), userId, "getBlockedRatings"); 902 final long identity = Binder.clearCallingIdentity(); 903 try { 904 synchronized (mLock) { 905 UserState userState = getUserStateLocked(resolvedUserId); 906 List<String> ratings = new ArrayList<String>(); 907 for (TvContentRating rating 908 : userState.persistentDataStore.getBlockedRatings()) { 909 ratings.add(rating.flattenToString()); 910 } 911 return ratings; 912 } 913 } finally { 914 Binder.restoreCallingIdentity(identity); 915 } 916 } 917 918 @Override 919 public void addBlockedRating(String rating, int userId) { 920 ensureParentalControlsPermission(); 921 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 922 Binder.getCallingUid(), userId, "addBlockedRating"); 923 final long identity = Binder.clearCallingIdentity(); 924 try { 925 synchronized (mLock) { 926 UserState userState = getUserStateLocked(resolvedUserId); 927 userState.persistentDataStore.addBlockedRating( 928 TvContentRating.unflattenFromString(rating)); 929 } 930 } finally { 931 Binder.restoreCallingIdentity(identity); 932 } 933 } 934 935 @Override 936 public void removeBlockedRating(String rating, int userId) { 937 ensureParentalControlsPermission(); 938 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 939 Binder.getCallingUid(), userId, "removeBlockedRating"); 940 final long identity = Binder.clearCallingIdentity(); 941 try { 942 synchronized (mLock) { 943 UserState userState = getUserStateLocked(resolvedUserId); 944 userState.persistentDataStore.removeBlockedRating( 945 TvContentRating.unflattenFromString(rating)); 946 } 947 } finally { 948 Binder.restoreCallingIdentity(identity); 949 } 950 } 951 952 private void ensureParentalControlsPermission() { 953 if (mContext.checkCallingPermission( 954 android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) 955 != PackageManager.PERMISSION_GRANTED) { 956 throw new SecurityException( 957 "The caller does not have parental controls permission"); 958 } 959 } 960 961 @Override 962 public void createSession(final ITvInputClient client, final String inputId, 963 int seq, int userId) { 964 final int callingUid = Binder.getCallingUid(); 965 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 966 userId, "createSession"); 967 final long identity = Binder.clearCallingIdentity(); 968 try { 969 synchronized (mLock) { 970 UserState userState = getUserStateLocked(resolvedUserId); 971 TvInputInfo info = userState.inputMap.get(inputId).mInfo; 972 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); 973 if (serviceState == null) { 974 serviceState = new ServiceState(info.getComponent(), resolvedUserId); 975 userState.serviceStateMap.put(info.getComponent(), serviceState); 976 } 977 // Send a null token immediately while reconnecting. 978 if (serviceState.mReconnecting == true) { 979 sendSessionTokenToClientLocked(client, inputId, null, null, seq); 980 return; 981 } 982 983 // Create a new session token and a session state. 984 IBinder sessionToken = new Binder(); 985 SessionState sessionState = new SessionState(sessionToken, info, client, 986 seq, callingUid, resolvedUserId); 987 988 // Add them to the global session state map of the current user. 989 userState.sessionStateMap.put(sessionToken, sessionState); 990 991 // Also, add them to the session state map of the current service. 992 serviceState.mSessionTokens.add(sessionToken); 993 994 if (serviceState.mService != null) { 995 createSessionInternalLocked(serviceState.mService, sessionToken, 996 resolvedUserId); 997 } else { 998 updateServiceConnectionLocked(info.getComponent(), resolvedUserId); 999 } 1000 } 1001 } finally { 1002 Binder.restoreCallingIdentity(identity); 1003 } 1004 } 1005 1006 @Override 1007 public void releaseSession(IBinder sessionToken, int userId) { 1008 final int callingUid = Binder.getCallingUid(); 1009 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1010 userId, "releaseSession"); 1011 final long identity = Binder.clearCallingIdentity(); 1012 try { 1013 synchronized (mLock) { 1014 releaseSessionLocked(sessionToken, callingUid, resolvedUserId); 1015 } 1016 } finally { 1017 Binder.restoreCallingIdentity(identity); 1018 } 1019 } 1020 1021 @Override 1022 public void setMainSession(IBinder sessionToken, int userId) { 1023 final int callingUid = Binder.getCallingUid(); 1024 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1025 userId, "setMainSession"); 1026 final long identity = Binder.clearCallingIdentity(); 1027 try { 1028 synchronized (mLock) { 1029 UserState userState = getUserStateLocked(resolvedUserId); 1030 if (sessionToken == userState.mainSessionToken) { 1031 return; 1032 } 1033 1034 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, 1035 resolvedUserId); 1036 ServiceState serviceState = getServiceStateLocked( 1037 sessionState.mInfo.getComponent(), resolvedUserId); 1038 ITvInputSession session = getSessionLocked(sessionState); 1039 1040 ServiceState prevMainServiceState = null; 1041 ITvInputSession prevMainSession = null; 1042 if (userState.mainSessionToken != null) { 1043 SessionState prevMainSessionState = getSessionStateLocked( 1044 userState.mainSessionToken, Process.SYSTEM_UID, resolvedUserId); 1045 prevMainServiceState = getServiceStateLocked( 1046 prevMainSessionState.mInfo.getComponent(), resolvedUserId); 1047 prevMainSession = getSessionLocked(prevMainSessionState); 1048 } 1049 1050 userState.mainSessionToken = sessionToken; 1051 1052 // Inform the new main session first. See {@link TvInputService#onSetMain}. 1053 if (serviceState.mIsHardware) { 1054 try { 1055 session.setMainSession(true); 1056 } catch (RemoteException e) { 1057 Slog.e(TAG, "error in setMainSession", e); 1058 } 1059 } 1060 if (prevMainSession != null && prevMainServiceState.mIsHardware) { 1061 try { 1062 prevMainSession.setMainSession(false); 1063 } catch (RemoteException e) { 1064 Slog.e(TAG, "error in setMainSession", e); 1065 } 1066 } 1067 } 1068 } finally { 1069 Binder.restoreCallingIdentity(identity); 1070 } 1071 } 1072 1073 @Override 1074 public void setSurface(IBinder sessionToken, Surface surface, int userId) { 1075 final int callingUid = Binder.getCallingUid(); 1076 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1077 userId, "setSurface"); 1078 final long identity = Binder.clearCallingIdentity(); 1079 try { 1080 synchronized (mLock) { 1081 try { 1082 getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface( 1083 surface); 1084 } catch (RemoteException e) { 1085 Slog.e(TAG, "error in setSurface", e); 1086 } 1087 } 1088 } finally { 1089 if (surface != null) { 1090 // surface is not used in TvInputManagerService. 1091 surface.release(); 1092 } 1093 Binder.restoreCallingIdentity(identity); 1094 } 1095 } 1096 1097 @Override 1098 public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width, 1099 int height, int userId) { 1100 final int callingUid = Binder.getCallingUid(); 1101 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1102 userId, "dispatchSurfaceChanged"); 1103 final long identity = Binder.clearCallingIdentity(); 1104 try { 1105 synchronized (mLock) { 1106 try { 1107 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1108 .dispatchSurfaceChanged(format, width, height); 1109 } catch (RemoteException e) { 1110 Slog.e(TAG, "error in dispatchSurfaceChanged", e); 1111 } 1112 } 1113 } finally { 1114 Binder.restoreCallingIdentity(identity); 1115 } 1116 } 1117 1118 @Override 1119 public void setVolume(IBinder sessionToken, float volume, int userId) { 1120 final int callingUid = Binder.getCallingUid(); 1121 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1122 userId, "setVolume"); 1123 final long identity = Binder.clearCallingIdentity(); 1124 try { 1125 synchronized (mLock) { 1126 try { 1127 getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume( 1128 volume); 1129 } catch (RemoteException e) { 1130 Slog.e(TAG, "error in setVolume", e); 1131 } 1132 } 1133 } finally { 1134 Binder.restoreCallingIdentity(identity); 1135 } 1136 } 1137 1138 @Override 1139 public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) { 1140 final int callingUid = Binder.getCallingUid(); 1141 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1142 userId, "tune"); 1143 final long identity = Binder.clearCallingIdentity(); 1144 try { 1145 synchronized (mLock) { 1146 try { 1147 getSessionLocked(sessionToken, callingUid, resolvedUserId).tune( 1148 channelUri, params); 1149 if (TvContract.isChannelUriForPassthroughTvInput(channelUri)) { 1150 // Do not log the watch history for passthrough inputs. 1151 return; 1152 } 1153 long currentTime = System.currentTimeMillis(); 1154 long channelId = ContentUris.parseId(channelUri); 1155 1156 // Close the open log entry first, if any. 1157 UserState userState = getUserStateLocked(resolvedUserId); 1158 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 1159 if (sessionState.mLogUri != null) { 1160 SomeArgs args = SomeArgs.obtain(); 1161 args.arg1 = sessionState.mLogUri; 1162 args.arg2 = currentTime; 1163 mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args) 1164 .sendToTarget(); 1165 } 1166 1167 // Create a log entry and fill it later. 1168 String packageName = sessionState.mInfo.getServiceInfo().packageName; 1169 ContentValues values = new ContentValues(); 1170 values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName); 1171 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 1172 currentTime); 1173 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0); 1174 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 1175 if (params != null) { 1176 values.put(TvContract.WatchedPrograms.COLUMN_TUNE_PARAMS, 1177 encodeTuneParams(params)); 1178 } 1179 1180 sessionState.mLogUri = mContentResolver.insert( 1181 TvContract.WatchedPrograms.CONTENT_URI, values); 1182 SomeArgs args = SomeArgs.obtain(); 1183 args.arg1 = sessionState.mLogUri; 1184 args.arg2 = ContentUris.parseId(channelUri); 1185 args.arg3 = currentTime; 1186 mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget(); 1187 } catch (RemoteException e) { 1188 Slog.e(TAG, "error in tune", e); 1189 return; 1190 } 1191 } 1192 } finally { 1193 Binder.restoreCallingIdentity(identity); 1194 } 1195 } 1196 1197 @Override 1198 public void requestUnblockContent( 1199 IBinder sessionToken, String unblockedRating, int userId) { 1200 final int callingUid = Binder.getCallingUid(); 1201 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1202 userId, "unblockContent"); 1203 final long identity = Binder.clearCallingIdentity(); 1204 try { 1205 synchronized (mLock) { 1206 try { 1207 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1208 .requestUnblockContent(unblockedRating); 1209 } catch (RemoteException e) { 1210 Slog.e(TAG, "error in unblockContent", e); 1211 } 1212 } 1213 } finally { 1214 Binder.restoreCallingIdentity(identity); 1215 } 1216 } 1217 1218 @Override 1219 public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) { 1220 final int callingUid = Binder.getCallingUid(); 1221 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1222 userId, "setCaptionEnabled"); 1223 final long identity = Binder.clearCallingIdentity(); 1224 try { 1225 synchronized (mLock) { 1226 try { 1227 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1228 .setCaptionEnabled(enabled); 1229 } catch (RemoteException e) { 1230 Slog.e(TAG, "error in setCaptionEnabled", e); 1231 } 1232 } 1233 } finally { 1234 Binder.restoreCallingIdentity(identity); 1235 } 1236 } 1237 1238 @Override 1239 public void selectTrack(IBinder sessionToken, TvTrackInfo track, int userId) { 1240 final int callingUid = Binder.getCallingUid(); 1241 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1242 userId, "selectTrack"); 1243 final long identity = Binder.clearCallingIdentity(); 1244 try { 1245 synchronized (mLock) { 1246 try { 1247 getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack( 1248 track); 1249 } catch (RemoteException e) { 1250 Slog.e(TAG, "error in selectTrack", e); 1251 } 1252 } 1253 } finally { 1254 Binder.restoreCallingIdentity(identity); 1255 } 1256 } 1257 1258 @Override 1259 public void unselectTrack(IBinder sessionToken, TvTrackInfo track, int userId) { 1260 final int callingUid = Binder.getCallingUid(); 1261 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1262 userId, "unselectTrack"); 1263 final long identity = Binder.clearCallingIdentity(); 1264 try { 1265 synchronized (mLock) { 1266 try { 1267 getSessionLocked(sessionToken, callingUid, resolvedUserId).unselectTrack( 1268 track); 1269 } catch (RemoteException e) { 1270 Slog.e(TAG, "error in unselectTrack", e); 1271 } 1272 } 1273 } finally { 1274 Binder.restoreCallingIdentity(identity); 1275 } 1276 } 1277 1278 @Override 1279 public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data, 1280 int userId) { 1281 final int callingUid = Binder.getCallingUid(); 1282 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1283 userId, "sendAppPrivateCommand"); 1284 final long identity = Binder.clearCallingIdentity(); 1285 try { 1286 synchronized (mLock) { 1287 try { 1288 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1289 .appPrivateCommand(command, data); 1290 } catch (RemoteException e) { 1291 Slog.e(TAG, "error in sendAppPrivateCommand", e); 1292 } 1293 } 1294 } finally { 1295 Binder.restoreCallingIdentity(identity); 1296 } 1297 } 1298 1299 @Override 1300 public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame, 1301 int userId) { 1302 final int callingUid = Binder.getCallingUid(); 1303 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1304 userId, "createOverlayView"); 1305 final long identity = Binder.clearCallingIdentity(); 1306 try { 1307 synchronized (mLock) { 1308 try { 1309 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1310 .createOverlayView(windowToken, frame); 1311 } catch (RemoteException e) { 1312 Slog.e(TAG, "error in createOverlayView", e); 1313 } 1314 } 1315 } finally { 1316 Binder.restoreCallingIdentity(identity); 1317 } 1318 } 1319 1320 @Override 1321 public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) { 1322 final int callingUid = Binder.getCallingUid(); 1323 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1324 userId, "relayoutOverlayView"); 1325 final long identity = Binder.clearCallingIdentity(); 1326 try { 1327 synchronized (mLock) { 1328 try { 1329 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1330 .relayoutOverlayView(frame); 1331 } catch (RemoteException e) { 1332 Slog.e(TAG, "error in relayoutOverlayView", e); 1333 } 1334 } 1335 } finally { 1336 Binder.restoreCallingIdentity(identity); 1337 } 1338 } 1339 1340 @Override 1341 public void removeOverlayView(IBinder sessionToken, int userId) { 1342 final int callingUid = Binder.getCallingUid(); 1343 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1344 userId, "removeOverlayView"); 1345 final long identity = Binder.clearCallingIdentity(); 1346 try { 1347 synchronized (mLock) { 1348 try { 1349 getSessionLocked(sessionToken, callingUid, resolvedUserId) 1350 .removeOverlayView(); 1351 } catch (RemoteException e) { 1352 Slog.e(TAG, "error in removeOverlayView", e); 1353 } 1354 } 1355 } finally { 1356 Binder.restoreCallingIdentity(identity); 1357 } 1358 } 1359 1360 @Override 1361 public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { 1362 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1363 != PackageManager.PERMISSION_GRANTED) { 1364 return null; 1365 } 1366 1367 final long identity = Binder.clearCallingIdentity(); 1368 try { 1369 return mTvInputHardwareManager.getHardwareList(); 1370 } finally { 1371 Binder.restoreCallingIdentity(identity); 1372 } 1373 } 1374 1375 @Override 1376 public ITvInputHardware acquireTvInputHardware(int deviceId, 1377 ITvInputHardwareCallback callback, TvInputInfo info, int userId) 1378 throws RemoteException { 1379 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1380 != PackageManager.PERMISSION_GRANTED) { 1381 return null; 1382 } 1383 1384 final long identity = Binder.clearCallingIdentity(); 1385 final int callingUid = Binder.getCallingUid(); 1386 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1387 userId, "acquireTvInputHardware"); 1388 try { 1389 return mTvInputHardwareManager.acquireHardware( 1390 deviceId, callback, info, callingUid, resolvedUserId); 1391 } finally { 1392 Binder.restoreCallingIdentity(identity); 1393 } 1394 } 1395 1396 @Override 1397 public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) 1398 throws RemoteException { 1399 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1400 != PackageManager.PERMISSION_GRANTED) { 1401 return; 1402 } 1403 1404 final long identity = Binder.clearCallingIdentity(); 1405 final int callingUid = Binder.getCallingUid(); 1406 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1407 userId, "releaseTvInputHardware"); 1408 try { 1409 mTvInputHardwareManager.releaseHardware( 1410 deviceId, hardware, callingUid, resolvedUserId); 1411 } finally { 1412 Binder.restoreCallingIdentity(identity); 1413 } 1414 } 1415 1416 @Override 1417 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId) 1418 throws RemoteException { 1419 if (mContext.checkCallingPermission( 1420 android.Manifest.permission.CAPTURE_TV_INPUT) 1421 != PackageManager.PERMISSION_GRANTED) { 1422 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 1423 } 1424 1425 final long identity = Binder.clearCallingIdentity(); 1426 final int callingUid = Binder.getCallingUid(); 1427 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1428 userId, "getAvailableTvStreamConfigList"); 1429 try { 1430 return mTvInputHardwareManager.getAvailableTvStreamConfigList( 1431 inputId, callingUid, resolvedUserId); 1432 } finally { 1433 Binder.restoreCallingIdentity(identity); 1434 } 1435 } 1436 1437 @Override 1438 public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config, 1439 int userId) 1440 throws RemoteException { 1441 if (mContext.checkCallingPermission( 1442 android.Manifest.permission.CAPTURE_TV_INPUT) 1443 != PackageManager.PERMISSION_GRANTED) { 1444 throw new SecurityException("Requires CAPTURE_TV_INPUT permission"); 1445 } 1446 1447 final long identity = Binder.clearCallingIdentity(); 1448 final int callingUid = Binder.getCallingUid(); 1449 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 1450 userId, "captureFrame"); 1451 try { 1452 final String wrappedInputId; 1453 synchronized (mLock) { 1454 UserState userState = getUserStateLocked(resolvedUserId); 1455 wrappedInputId = userState.wrappedInputMap.get(inputId); 1456 } 1457 return mTvInputHardwareManager.captureFrame( 1458 (wrappedInputId != null) ? wrappedInputId : inputId, 1459 surface, config, callingUid, resolvedUserId); 1460 } finally { 1461 Binder.restoreCallingIdentity(identity); 1462 } 1463 } 1464 1465 @Override 1466 @SuppressWarnings("resource") 1467 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 1468 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 1469 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1470 != PackageManager.PERMISSION_GRANTED) { 1471 pw.println("Permission Denial: can't dump TvInputManager from pid=" 1472 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 1473 return; 1474 } 1475 1476 synchronized (mLock) { 1477 pw.println("User Ids (Current user: " + mCurrentUserId + "):"); 1478 pw.increaseIndent(); 1479 for (int i = 0; i < mUserStates.size(); i++) { 1480 int userId = mUserStates.keyAt(i); 1481 pw.println(Integer.valueOf(userId)); 1482 } 1483 pw.decreaseIndent(); 1484 1485 for (int i = 0; i < mUserStates.size(); i++) { 1486 int userId = mUserStates.keyAt(i); 1487 UserState userState = getUserStateLocked(userId); 1488 pw.println("UserState (" + userId + "):"); 1489 pw.increaseIndent(); 1490 1491 pw.println("inputMap: inputId -> TvInputState"); 1492 pw.increaseIndent(); 1493 for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) { 1494 pw.println(entry.getKey() + ": " + entry.getValue()); 1495 } 1496 pw.decreaseIndent(); 1497 1498 pw.println("packageSet:"); 1499 pw.increaseIndent(); 1500 for (String packageName : userState.packageSet) { 1501 pw.println(packageName); 1502 } 1503 pw.decreaseIndent(); 1504 1505 pw.println("clientStateMap: ITvInputClient -> ClientState"); 1506 pw.increaseIndent(); 1507 for (Map.Entry<IBinder, ClientState> entry : 1508 userState.clientStateMap.entrySet()) { 1509 ClientState client = entry.getValue(); 1510 pw.println(entry.getKey() + ": " + client); 1511 1512 pw.increaseIndent(); 1513 1514 pw.println("mSessionTokens:"); 1515 pw.increaseIndent(); 1516 for (IBinder token : client.mSessionTokens) { 1517 pw.println("" + token); 1518 } 1519 pw.decreaseIndent(); 1520 1521 pw.println("mClientTokens: " + client.mClientToken); 1522 pw.println("mUserId: " + client.mUserId); 1523 1524 pw.decreaseIndent(); 1525 } 1526 pw.decreaseIndent(); 1527 1528 pw.println("serviceStateMap: ComponentName -> ServiceState"); 1529 pw.increaseIndent(); 1530 for (Map.Entry<ComponentName, ServiceState> entry : 1531 userState.serviceStateMap.entrySet()) { 1532 ServiceState service = entry.getValue(); 1533 pw.println(entry.getKey() + ": " + service); 1534 1535 pw.increaseIndent(); 1536 1537 pw.println("mClientTokens:"); 1538 pw.increaseIndent(); 1539 for (IBinder token : service.mClientTokens) { 1540 pw.println("" + token); 1541 } 1542 pw.decreaseIndent(); 1543 1544 pw.println("mSessionTokens:"); 1545 pw.increaseIndent(); 1546 for (IBinder token : service.mSessionTokens) { 1547 pw.println("" + token); 1548 } 1549 pw.decreaseIndent(); 1550 1551 pw.println("mService: " + service.mService); 1552 pw.println("mCallback: " + service.mCallback); 1553 pw.println("mBound: " + service.mBound); 1554 pw.println("mReconnecting: " + service.mReconnecting); 1555 1556 pw.decreaseIndent(); 1557 } 1558 pw.decreaseIndent(); 1559 1560 pw.println("sessionStateMap: ITvInputSession -> SessionState"); 1561 pw.increaseIndent(); 1562 for (Map.Entry<IBinder, SessionState> entry : 1563 userState.sessionStateMap.entrySet()) { 1564 SessionState session = entry.getValue(); 1565 pw.println(entry.getKey() + ": " + session); 1566 1567 pw.increaseIndent(); 1568 pw.println("mInfo: " + session.mInfo); 1569 pw.println("mClient: " + session.mClient); 1570 pw.println("mSeq: " + session.mSeq); 1571 pw.println("mCallingUid: " + session.mCallingUid); 1572 pw.println("mUserId: " + session.mUserId); 1573 pw.println("mSessionToken: " + session.mSessionToken); 1574 pw.println("mSession: " + session.mSession); 1575 pw.println("mLogUri: " + session.mLogUri); 1576 pw.decreaseIndent(); 1577 } 1578 pw.decreaseIndent(); 1579 1580 pw.println("callbackSet:"); 1581 pw.increaseIndent(); 1582 for (ITvInputManagerCallback callback : userState.callbackSet) { 1583 pw.println(callback.toString()); 1584 } 1585 pw.decreaseIndent(); 1586 1587 pw.decreaseIndent(); 1588 } 1589 } 1590 } 1591 1592 private String encodeTuneParams(Bundle tuneParams) { 1593 StringBuilder builder = new StringBuilder(); 1594 Set<String> keySet = tuneParams.keySet(); 1595 Iterator<String> it = keySet.iterator(); 1596 while (it.hasNext()) { 1597 String key = it.next(); 1598 Object value = tuneParams.get(key); 1599 if (value == null) { 1600 continue; 1601 } 1602 builder.append(replaceEscapeCharacters(key)); 1603 builder.append("="); 1604 builder.append(replaceEscapeCharacters(value.toString())); 1605 if (it.hasNext()) { 1606 builder.append(", "); 1607 } 1608 } 1609 return builder.toString(); 1610 } 1611 1612 private String replaceEscapeCharacters(String src) { 1613 final char ESCAPE_CHARACTER = '%'; 1614 final String ENCODING_TARGET_CHARACTERS = "%=,"; 1615 StringBuilder builder = new StringBuilder(); 1616 for (char ch : src.toCharArray()) { 1617 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) { 1618 builder.append(ESCAPE_CHARACTER); 1619 } 1620 builder.append(ch); 1621 } 1622 return builder.toString(); 1623 } 1624 } 1625 1626 private static final class TvInputState { 1627 // A TvInputInfo object which represents the TV input. 1628 private TvInputInfo mInfo; 1629 1630 // The state of TV input. Connected by default. 1631 private int mState = INPUT_STATE_CONNECTED; 1632 1633 @Override 1634 public String toString() { 1635 return "mInfo: " + mInfo + "; mState: " + mState; 1636 } 1637 } 1638 1639 private static final class UserState { 1640 // A mapping from the TV input id to its TvInputState. 1641 private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>(); 1642 1643 // A set of all TV input packages. 1644 private final Set<String> packageSet = new HashSet<String>(); 1645 1646 // A mapping from the token of a client to its state. 1647 private final Map<IBinder, ClientState> clientStateMap = 1648 new HashMap<IBinder, ClientState>(); 1649 1650 // A mapping from the name of a TV input service to its state. 1651 private final Map<ComponentName, ServiceState> serviceStateMap = 1652 new HashMap<ComponentName, ServiceState>(); 1653 1654 // A mapping from the token of a TV input session to its state. 1655 private final Map<IBinder, SessionState> sessionStateMap = 1656 new HashMap<IBinder, SessionState>(); 1657 1658 // A set of callbacks. 1659 private final Set<ITvInputManagerCallback> callbackSet = 1660 new HashSet<ITvInputManagerCallback>(); 1661 1662 // A mapping from the TV input id to wrapped input id. 1663 private final Map<String, String> wrappedInputMap = new HashMap<String, String>(); 1664 1665 // The token of a "main" TV input session. 1666 private IBinder mainSessionToken = null; 1667 1668 // Persistent data store for all internal settings maintained by the TV input manager 1669 // service. 1670 private final PersistentDataStore persistentDataStore; 1671 1672 private UserState(Context context, int userId) { 1673 persistentDataStore = new PersistentDataStore(context, userId); 1674 } 1675 } 1676 1677 private final class ClientState implements IBinder.DeathRecipient { 1678 private final List<IBinder> mSessionTokens = new ArrayList<IBinder>(); 1679 1680 private IBinder mClientToken; 1681 private final int mUserId; 1682 1683 ClientState(IBinder clientToken, int userId) { 1684 mClientToken = clientToken; 1685 mUserId = userId; 1686 } 1687 1688 public boolean isEmpty() { 1689 return mSessionTokens.isEmpty(); 1690 } 1691 1692 @Override 1693 public void binderDied() { 1694 synchronized (mLock) { 1695 UserState userState = getUserStateLocked(mUserId); 1696 // DO NOT remove the client state of clientStateMap in this method. It will be 1697 // removed in releaseSessionLocked(). 1698 ClientState clientState = userState.clientStateMap.get(mClientToken); 1699 if (clientState != null) { 1700 while (clientState.mSessionTokens.size() > 0) { 1701 releaseSessionLocked( 1702 clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId); 1703 } 1704 } 1705 mClientToken = null; 1706 } 1707 } 1708 } 1709 1710 private final class ServiceState { 1711 private final List<IBinder> mClientTokens = new ArrayList<IBinder>(); 1712 private final List<IBinder> mSessionTokens = new ArrayList<IBinder>(); 1713 private final ServiceConnection mConnection; 1714 private final ComponentName mComponent; 1715 private final boolean mIsHardware; 1716 private final List<TvInputInfo> mInputList = new ArrayList<TvInputInfo>(); 1717 1718 private ITvInputService mService; 1719 private ServiceCallback mCallback; 1720 private boolean mBound; 1721 private boolean mReconnecting; 1722 1723 private ServiceState(ComponentName component, int userId) { 1724 mComponent = component; 1725 mConnection = new InputServiceConnection(component, userId); 1726 mIsHardware = hasHardwarePermission(mContext.getPackageManager(), mComponent); 1727 } 1728 } 1729 1730 private final class SessionState implements IBinder.DeathRecipient { 1731 private final TvInputInfo mInfo; 1732 private final ITvInputClient mClient; 1733 private final int mSeq; 1734 private final int mCallingUid; 1735 private final int mUserId; 1736 private final IBinder mSessionToken; 1737 private ITvInputSession mSession; 1738 private Uri mLogUri; 1739 1740 private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client, int seq, 1741 int callingUid, int userId) { 1742 mSessionToken = sessionToken; 1743 mInfo = info; 1744 mClient = client; 1745 mSeq = seq; 1746 mCallingUid = callingUid; 1747 mUserId = userId; 1748 } 1749 1750 @Override 1751 public void binderDied() { 1752 synchronized (mLock) { 1753 mSession = null; 1754 if (mClient != null) { 1755 try { 1756 mClient.onSessionReleased(mSeq); 1757 } catch(RemoteException e) { 1758 Slog.e(TAG, "error in onSessionReleased", e); 1759 } 1760 } 1761 removeSessionStateLocked(mSessionToken, mUserId); 1762 } 1763 } 1764 } 1765 1766 private final class InputServiceConnection implements ServiceConnection { 1767 private final ComponentName mComponent; 1768 private final int mUserId; 1769 1770 private InputServiceConnection(ComponentName component, int userId) { 1771 mComponent = component; 1772 mUserId = userId; 1773 } 1774 1775 @Override 1776 public void onServiceConnected(ComponentName component, IBinder service) { 1777 if (DEBUG) { 1778 Slog.d(TAG, "onServiceConnected(component=" + component + ")"); 1779 } 1780 synchronized (mLock) { 1781 UserState userState = getUserStateLocked(mUserId); 1782 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 1783 serviceState.mService = ITvInputService.Stub.asInterface(service); 1784 1785 // Register a callback, if we need to. 1786 if (serviceState.mIsHardware && serviceState.mCallback == null) { 1787 serviceState.mCallback = new ServiceCallback(mComponent, mUserId); 1788 try { 1789 serviceState.mService.registerCallback(serviceState.mCallback); 1790 } catch (RemoteException e) { 1791 Slog.e(TAG, "error in registerCallback", e); 1792 } 1793 } 1794 1795 // And create sessions, if any. 1796 for (IBinder sessionToken : serviceState.mSessionTokens) { 1797 createSessionInternalLocked(serviceState.mService, sessionToken, mUserId); 1798 } 1799 1800 for (TvInputState inputState : userState.inputMap.values()) { 1801 if (inputState.mInfo.getComponent().equals(component) 1802 && inputState.mState != INPUT_STATE_DISCONNECTED) { 1803 notifyInputStateChangedLocked(userState, inputState.mInfo.getId(), 1804 inputState.mState, null); 1805 } 1806 } 1807 1808 if (serviceState.mIsHardware) { 1809 List<TvInputHardwareInfo> hardwareInfoList = 1810 mTvInputHardwareManager.getHardwareList(); 1811 for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) { 1812 try { 1813 serviceState.mService.notifyHardwareAdded(hardwareInfo); 1814 } catch (RemoteException e) { 1815 Slog.e(TAG, "error in notifyHardwareAdded", e); 1816 } 1817 } 1818 1819 List<HdmiCecDeviceInfo> cecDeviceInfoList = 1820 mTvInputHardwareManager.getHdmiCecInputDeviceList(); 1821 for (HdmiCecDeviceInfo cecDeviceInfo : cecDeviceInfoList) { 1822 try { 1823 serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo); 1824 } catch (RemoteException e) { 1825 Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e); 1826 } 1827 } 1828 } 1829 } 1830 } 1831 1832 @Override 1833 public void onServiceDisconnected(ComponentName component) { 1834 if (DEBUG) { 1835 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")"); 1836 } 1837 if (!mComponent.equals(component)) { 1838 throw new IllegalArgumentException("Mismatched ComponentName: " 1839 + mComponent + " (expected), " + component + " (actual)."); 1840 } 1841 synchronized (mLock) { 1842 UserState userState = getUserStateLocked(mUserId); 1843 ServiceState serviceState = userState.serviceStateMap.get(mComponent); 1844 if (serviceState != null) { 1845 serviceState.mReconnecting = true; 1846 serviceState.mBound = false; 1847 serviceState.mService = null; 1848 serviceState.mCallback = null; 1849 1850 // Send null tokens for not finishing create session events. 1851 for (IBinder sessionToken : serviceState.mSessionTokens) { 1852 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 1853 if (sessionState.mSession == null) { 1854 removeSessionStateLocked(sessionToken, sessionState.mUserId); 1855 sendSessionTokenToClientLocked(sessionState.mClient, 1856 sessionState.mInfo.getId(), null, null, sessionState.mSeq); 1857 } 1858 } 1859 1860 for (TvInputState inputState : userState.inputMap.values()) { 1861 if (inputState.mInfo.getComponent().equals(component)) { 1862 String inputId = inputState.mInfo.getId(); 1863 notifyInputStateChangedLocked(userState, inputId, 1864 INPUT_STATE_DISCONNECTED, null); 1865 userState.wrappedInputMap.remove(inputId); 1866 } 1867 } 1868 updateServiceConnectionLocked(mComponent, mUserId); 1869 } 1870 } 1871 } 1872 } 1873 1874 private final class ServiceCallback extends ITvInputServiceCallback.Stub { 1875 private final ComponentName mComponent; 1876 private final int mUserId; 1877 1878 ServiceCallback(ComponentName component, int userId) { 1879 mComponent = component; 1880 mUserId = userId; 1881 } 1882 1883 private void ensureHardwarePermission() { 1884 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 1885 != PackageManager.PERMISSION_GRANTED) { 1886 throw new SecurityException("The caller does not have hardware permission"); 1887 } 1888 } 1889 1890 private void ensureValidInput(TvInputInfo inputInfo) { 1891 if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) { 1892 throw new IllegalArgumentException("Invalid TvInputInfo"); 1893 } 1894 } 1895 1896 private void addTvInputLocked(TvInputInfo inputInfo) { 1897 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 1898 serviceState.mInputList.add(inputInfo); 1899 buildTvInputListLocked(mUserId); 1900 } 1901 1902 @Override 1903 public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) { 1904 ensureHardwarePermission(); 1905 ensureValidInput(inputInfo); 1906 synchronized (mLock) { 1907 mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo); 1908 addTvInputLocked(inputInfo); 1909 } 1910 } 1911 1912 @Override 1913 public void addHdmiCecTvInput(int logicalAddress, TvInputInfo inputInfo) { 1914 ensureHardwarePermission(); 1915 ensureValidInput(inputInfo); 1916 synchronized (mLock) { 1917 mTvInputHardwareManager.addHdmiCecTvInput(logicalAddress, inputInfo); 1918 addTvInputLocked(inputInfo); 1919 } 1920 } 1921 1922 @Override 1923 public void removeTvInput(String inputId) { 1924 ensureHardwarePermission(); 1925 synchronized (mLock) { 1926 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 1927 boolean removed = false; 1928 for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator(); 1929 it.hasNext(); ) { 1930 if (it.next().getId().equals(inputId)) { 1931 it.remove(); 1932 removed = true; 1933 break; 1934 } 1935 } 1936 if (removed) { 1937 buildTvInputListLocked(mUserId); 1938 mTvInputHardwareManager.removeTvInput(inputId); 1939 } else { 1940 Slog.e(TAG, "TvInputInfo with inputId=" + inputId + " not found."); 1941 } 1942 } 1943 } 1944 1945 @Override 1946 public void setWrappedInputId(String inputId, String wrappedInputId) { 1947 synchronized (mLock) { 1948 if (!hasInputIdLocked(inputId)) { 1949 return; 1950 } 1951 UserState userState = getUserStateLocked(mUserId); 1952 userState.wrappedInputMap.put(inputId, wrappedInputId); 1953 } 1954 } 1955 1956 private boolean hasInputIdLocked(String inputId) { 1957 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); 1958 for (TvInputInfo inputInfo : serviceState.mInputList) { 1959 if (inputInfo.getId().equals(inputId)) { 1960 return true; 1961 } 1962 } 1963 return false; 1964 } 1965 } 1966 1967 private final class LogHandler extends Handler { 1968 private static final int MSG_OPEN_ENTRY = 1; 1969 private static final int MSG_UPDATE_ENTRY = 2; 1970 private static final int MSG_CLOSE_ENTRY = 3; 1971 1972 public LogHandler(Looper looper) { 1973 super(looper); 1974 } 1975 1976 @Override 1977 public void handleMessage(Message msg) { 1978 switch (msg.what) { 1979 case MSG_OPEN_ENTRY: { 1980 SomeArgs args = (SomeArgs) msg.obj; 1981 Uri uri = (Uri) args.arg1; 1982 long channelId = (long) args.arg2; 1983 long time = (long) args.arg3; 1984 onOpenEntry(uri, channelId, time); 1985 args.recycle(); 1986 return; 1987 } 1988 case MSG_UPDATE_ENTRY: { 1989 SomeArgs args = (SomeArgs) msg.obj; 1990 Uri uri = (Uri) args.arg1; 1991 long channelId = (long) args.arg2; 1992 long time = (long) args.arg3; 1993 onUpdateEntry(uri, channelId, time); 1994 args.recycle(); 1995 return; 1996 } 1997 case MSG_CLOSE_ENTRY: { 1998 SomeArgs args = (SomeArgs) msg.obj; 1999 Uri uri = (Uri) args.arg1; 2000 long time = (long) args.arg2; 2001 onCloseEntry(uri, time); 2002 args.recycle(); 2003 return; 2004 } 2005 default: { 2006 Slog.w(TAG, "Unhandled message code: " + msg.what); 2007 return; 2008 } 2009 } 2010 } 2011 2012 private void onOpenEntry(Uri uri, long channelId, long watchStarttime) { 2013 String[] projection = { 2014 TvContract.Programs.COLUMN_TITLE, 2015 TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, 2016 TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, 2017 TvContract.Programs.COLUMN_SHORT_DESCRIPTION 2018 }; 2019 String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND " 2020 + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND " 2021 + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS + ">?"; 2022 String[] selectionArgs = { 2023 String.valueOf(channelId), 2024 String.valueOf(watchStarttime), 2025 String.valueOf(watchStarttime) 2026 }; 2027 String sortOrder = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC"; 2028 Cursor cursor = null; 2029 try { 2030 cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection, 2031 selection, selectionArgs, sortOrder); 2032 if (cursor != null && cursor.moveToNext()) { 2033 ContentValues values = new ContentValues(); 2034 values.put(TvContract.WatchedPrograms.COLUMN_TITLE, cursor.getString(0)); 2035 values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, 2036 cursor.getLong(1)); 2037 long endTime = cursor.getLong(2); 2038 values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); 2039 values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, cursor.getString(3)); 2040 mContentResolver.update(uri, values, null, null); 2041 2042 // Schedule an update when the current program ends. 2043 SomeArgs args = SomeArgs.obtain(); 2044 args.arg1 = uri; 2045 args.arg2 = channelId; 2046 args.arg3 = endTime; 2047 Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args); 2048 sendMessageDelayed(msg, endTime - System.currentTimeMillis()); 2049 } 2050 } finally { 2051 if (cursor != null) { 2052 cursor.close(); 2053 } 2054 } 2055 } 2056 2057 private void onUpdateEntry(Uri uri, long channelId, long time) { 2058 String[] projection = { 2059 TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 2060 TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 2061 TvContract.WatchedPrograms.COLUMN_TITLE, 2062 TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, 2063 TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, 2064 TvContract.WatchedPrograms.COLUMN_DESCRIPTION 2065 }; 2066 Cursor cursor = null; 2067 try { 2068 cursor = mContentResolver.query(uri, projection, null, null, null); 2069 if (cursor != null && cursor.moveToNext()) { 2070 long watchStartTime = cursor.getLong(0); 2071 long watchEndTime = cursor.getLong(1); 2072 String title = cursor.getString(2); 2073 long startTime = cursor.getLong(3); 2074 long endTime = cursor.getLong(4); 2075 String description = cursor.getString(5); 2076 2077 // Do nothing if the current log entry is already closed. 2078 if (watchEndTime > 0) { 2079 return; 2080 } 2081 2082 // The current program has just ended. Create a (complete) log entry off the 2083 // current entry. 2084 ContentValues values = new ContentValues(); 2085 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 2086 watchStartTime); 2087 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, time); 2088 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 2089 values.put(TvContract.WatchedPrograms.COLUMN_TITLE, title); 2090 values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime); 2091 values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); 2092 values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, description); 2093 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 2094 } 2095 } finally { 2096 if (cursor != null) { 2097 cursor.close(); 2098 } 2099 } 2100 // Re-open the current log entry with the next program information. 2101 onOpenEntry(uri, channelId, time); 2102 } 2103 2104 private void onCloseEntry(Uri uri, long watchEndTime) { 2105 ContentValues values = new ContentValues(); 2106 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, watchEndTime); 2107 mContentResolver.update(uri, values, null, null); 2108 } 2109 } 2110 2111 final class HardwareListener implements TvInputHardwareManager.Listener { 2112 @Override 2113 public void onStateChanged(String inputId, int state) { 2114 synchronized (mLock) { 2115 setStateLocked(inputId, state, mCurrentUserId); 2116 } 2117 } 2118 2119 @Override 2120 public void onHardwareDeviceAdded(TvInputHardwareInfo info) { 2121 synchronized (mLock) { 2122 UserState userState = getUserStateLocked(mCurrentUserId); 2123 // Broadcast the event to all hardware inputs. 2124 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2125 if (!serviceState.mIsHardware || serviceState.mService == null) continue; 2126 try { 2127 serviceState.mService.notifyHardwareAdded(info); 2128 } catch (RemoteException e) { 2129 Slog.e(TAG, "error in notifyHardwareAdded", e); 2130 } 2131 } 2132 } 2133 } 2134 2135 @Override 2136 public void onHardwareDeviceRemoved(TvInputHardwareInfo info) { 2137 synchronized (mLock) { 2138 UserState userState = getUserStateLocked(mCurrentUserId); 2139 // Broadcast the event to all hardware inputs. 2140 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2141 if (!serviceState.mIsHardware || serviceState.mService == null) continue; 2142 try { 2143 serviceState.mService.notifyHardwareRemoved(info); 2144 } catch (RemoteException e) { 2145 Slog.e(TAG, "error in notifyHardwareRemoved", e); 2146 } 2147 } 2148 } 2149 } 2150 2151 @Override 2152 public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDeviceInfo) { 2153 synchronized (mLock) { 2154 UserState userState = getUserStateLocked(mCurrentUserId); 2155 // Broadcast the event to all hardware inputs. 2156 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2157 if (!serviceState.mIsHardware || serviceState.mService == null) continue; 2158 try { 2159 serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo); 2160 } catch (RemoteException e) { 2161 Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e); 2162 } 2163 } 2164 } 2165 } 2166 2167 @Override 2168 public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDeviceInfo) { 2169 synchronized (mLock) { 2170 UserState userState = getUserStateLocked(mCurrentUserId); 2171 // Broadcast the event to all hardware inputs. 2172 for (ServiceState serviceState : userState.serviceStateMap.values()) { 2173 if (!serviceState.mIsHardware || serviceState.mService == null) continue; 2174 try { 2175 serviceState.mService.notifyHdmiCecDeviceRemoved(cecDeviceInfo); 2176 } catch (RemoteException e) { 2177 Slog.e(TAG, "error in notifyHdmiCecDeviceRemoved", e); 2178 } 2179 } 2180 } 2181 } 2182 } 2183} 2184