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