TvInputManagerService.java revision 0ceb7e4755015eafda29c251eac285620788a51b
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 android.app.ActivityManager; 20import android.content.BroadcastReceiver; 21import android.content.ComponentName; 22import android.content.ContentResolver; 23import android.content.ContentUris; 24import android.content.ContentValues; 25import android.content.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.ServiceConnection; 29import android.content.pm.PackageManager; 30import android.content.pm.ResolveInfo; 31import android.content.pm.ServiceInfo; 32import android.database.Cursor; 33import android.graphics.Rect; 34import android.media.tv.ITvInputClient; 35import android.media.tv.ITvInputHardware; 36import android.media.tv.ITvInputHardwareCallback; 37import android.media.tv.ITvInputManager; 38import android.media.tv.ITvInputService; 39import android.media.tv.ITvInputServiceCallback; 40import android.media.tv.ITvInputSession; 41import android.media.tv.ITvInputSessionCallback; 42import android.media.tv.TvContract; 43import android.media.tv.TvInputHardwareInfo; 44import android.media.tv.TvInputInfo; 45import android.media.tv.TvInputService; 46import android.net.Uri; 47import android.os.Binder; 48import android.os.Bundle; 49import android.os.Handler; 50import android.os.IBinder; 51import android.os.Looper; 52import android.os.Message; 53import android.os.Process; 54import android.os.RemoteException; 55import android.os.UserHandle; 56import android.util.Slog; 57import android.util.SparseArray; 58import android.view.InputChannel; 59import android.view.Surface; 60 61import com.android.internal.content.PackageMonitor; 62import com.android.internal.os.SomeArgs; 63import com.android.server.IoThread; 64import com.android.server.SystemService; 65 66import org.xmlpull.v1.XmlPullParserException; 67 68import java.io.IOException; 69 70import java.util.ArrayList; 71import java.util.HashMap; 72import java.util.List; 73import java.util.Map; 74 75/** This class provides a system service that manages television inputs. */ 76public final class TvInputManagerService extends SystemService { 77 // STOPSHIP: Turn debugging off. 78 private static final boolean DEBUG = true; 79 private static final String TAG = "TvInputManagerService"; 80 81 private final Context mContext; 82 private final TvInputHardwareManager mTvInputHardwareManager; 83 84 private final ContentResolver mContentResolver; 85 86 // A global lock. 87 private final Object mLock = new Object(); 88 89 // ID of the current user. 90 private int mCurrentUserId = UserHandle.USER_OWNER; 91 92 // A map from user id to UserState. 93 private final SparseArray<UserState> mUserStates = new SparseArray<UserState>(); 94 95 private final Handler mLogHandler; 96 97 public TvInputManagerService(Context context) { 98 super(context); 99 100 mContext = context; 101 mContentResolver = context.getContentResolver(); 102 mLogHandler = new LogHandler(IoThread.get().getLooper()); 103 104 mTvInputHardwareManager = new TvInputHardwareManager(context); 105 106 synchronized (mLock) { 107 mUserStates.put(mCurrentUserId, new UserState()); 108 } 109 } 110 111 @Override 112 public void onStart() { 113 publishBinderService(Context.TV_INPUT_SERVICE, new BinderService()); 114 } 115 116 @Override 117 public void onBootPhase(int phase) { 118 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 119 registerBroadcastReceivers(); 120 synchronized (mLock) { 121 buildTvInputListLocked(mCurrentUserId); 122 } 123 } 124 } 125 126 private void registerBroadcastReceivers() { 127 PackageMonitor monitor = new PackageMonitor() { 128 @Override 129 public void onSomePackagesChanged() { 130 synchronized (mLock) { 131 buildTvInputListLocked(mCurrentUserId); 132 } 133 } 134 }; 135 monitor.register(mContext, null, UserHandle.ALL, true); 136 137 IntentFilter intentFilter = new IntentFilter(); 138 intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 139 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 140 mContext.registerReceiverAsUser(new BroadcastReceiver() { 141 @Override 142 public void onReceive(Context context, Intent intent) { 143 String action = intent.getAction(); 144 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 145 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 146 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 147 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 148 } 149 } 150 }, UserHandle.ALL, intentFilter, null, null); 151 } 152 153 private void buildTvInputListLocked(int userId) { 154 UserState userState = getUserStateLocked(userId); 155 userState.inputMap.clear(); 156 157 if (DEBUG) Slog.d(TAG, "buildTvInputList"); 158 PackageManager pm = mContext.getPackageManager(); 159 List<ResolveInfo> services = pm.queryIntentServices( 160 new Intent(TvInputService.SERVICE_INTERFACE), 161 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); 162 for (ResolveInfo ri : services) { 163 ServiceInfo si = ri.serviceInfo; 164 if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { 165 Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission " 166 + android.Manifest.permission.BIND_TV_INPUT); 167 continue; 168 } 169 try { 170 TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri); 171 if (DEBUG) Slog.d(TAG, "add " + info.getId()); 172 userState.inputMap.put(info.getId(), info); 173 } catch (IOException | XmlPullParserException e) { 174 Slog.e(TAG, "Can't load TV input " + si.name, e); 175 } 176 } 177 } 178 179 private void switchUser(int userId) { 180 synchronized (mLock) { 181 if (mCurrentUserId == userId) { 182 return; 183 } 184 // final int oldUserId = mCurrentUserId; 185 // TODO: Release services and sessions in the old user state, if needed. 186 mCurrentUserId = userId; 187 188 UserState userState = mUserStates.get(userId); 189 if (userState == null) { 190 userState = new UserState(); 191 } 192 mUserStates.put(userId, userState); 193 buildTvInputListLocked(userId); 194 } 195 } 196 197 private void removeUser(int userId) { 198 synchronized (mLock) { 199 UserState userState = mUserStates.get(userId); 200 if (userState == null) { 201 return; 202 } 203 // Release created sessions. 204 for (SessionState state : userState.sessionStateMap.values()) { 205 if (state.mSession != null) { 206 try { 207 state.mSession.release(); 208 } catch (RemoteException e) { 209 Slog.e(TAG, "error in release", e); 210 } 211 } 212 } 213 userState.sessionStateMap.clear(); 214 215 // Unregister all callbacks and unbind all services. 216 for (ServiceState serviceState : userState.serviceStateMap.values()) { 217 if (serviceState.mCallback != null) { 218 try { 219 serviceState.mService.unregisterCallback(serviceState.mCallback); 220 } catch (RemoteException e) { 221 Slog.e(TAG, "error in unregisterCallback", e); 222 } 223 } 224 serviceState.mClientTokens.clear(); 225 mContext.unbindService(serviceState.mConnection); 226 } 227 userState.serviceStateMap.clear(); 228 229 userState.clientStateMap.clear(); 230 231 mUserStates.remove(userId); 232 } 233 } 234 235 private UserState getUserStateLocked(int userId) { 236 UserState userState = mUserStates.get(userId); 237 if (userState == null) { 238 throw new IllegalStateException("User state not found for user ID " + userId); 239 } 240 return userState; 241 } 242 243 private ServiceState getServiceStateLocked(String inputId, int userId) { 244 UserState userState = getUserStateLocked(userId); 245 ServiceState serviceState = userState.serviceStateMap.get(inputId); 246 if (serviceState == null) { 247 throw new IllegalStateException("Service state not found for " + inputId + " (userId=" 248 + userId + ")"); 249 } 250 return serviceState; 251 } 252 253 private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) { 254 UserState userState = getUserStateLocked(userId); 255 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 256 if (sessionState == null) { 257 throw new IllegalArgumentException("Session state not found for token " + sessionToken); 258 } 259 // Only the application that requested this session or the system can access it. 260 if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) { 261 throw new SecurityException("Illegal access to the session with token " + sessionToken 262 + " from uid " + callingUid); 263 } 264 return sessionState; 265 } 266 267 private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) { 268 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 269 ITvInputSession session = sessionState.mSession; 270 if (session == null) { 271 throw new IllegalStateException("Session not yet created for token " + sessionToken); 272 } 273 return session; 274 } 275 276 private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId, 277 String methodName) { 278 return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false, 279 false, methodName, null); 280 } 281 282 private void updateServiceConnectionLocked(String inputId, int userId) { 283 UserState userState = getUserStateLocked(userId); 284 ServiceState serviceState = userState.serviceStateMap.get(inputId); 285 if (serviceState == null) { 286 return; 287 } 288 if (serviceState.mReconnecting) { 289 if (!serviceState.mSessionTokens.isEmpty()) { 290 // wait until all the sessions are removed. 291 return; 292 } 293 serviceState.mReconnecting = false; 294 } 295 boolean isStateEmpty = serviceState.mClientTokens.isEmpty() 296 && serviceState.mSessionTokens.isEmpty(); 297 if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) { 298 // This means that the service is not yet connected but its state indicates that we 299 // have pending requests. Then, connect the service. 300 if (serviceState.mBound) { 301 // We have already bound to the service so we don't try to bind again until after we 302 // unbind later on. 303 return; 304 } 305 if (DEBUG) { 306 Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId 307 + ")"); 308 } 309 310 Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent( 311 userState.inputMap.get(inputId).getComponent()); 312 serviceState.mBound = mContext.bindServiceAsUser( 313 i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId)); 314 } else if (serviceState.mService != null && isStateEmpty) { 315 // This means that the service is already connected but its state indicates that we have 316 // nothing to do with it. Then, disconnect the service. 317 if (DEBUG) { 318 Slog.d(TAG, "unbindService(inputId=" + inputId + ")"); 319 } 320 mContext.unbindService(serviceState.mConnection); 321 userState.serviceStateMap.remove(inputId); 322 } 323 } 324 325 private ClientState createClientStateLocked(IBinder clientToken, int userId) { 326 UserState userState = getUserStateLocked(userId); 327 ClientState clientState = new ClientState(clientToken, userId); 328 try { 329 clientToken.linkToDeath(clientState, 0); 330 } catch (RemoteException e) { 331 Slog.e(TAG, "Client is already died."); 332 } 333 userState.clientStateMap.put(clientToken, clientState); 334 return clientState; 335 } 336 337 private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken, 338 final int userId) { 339 final UserState userState = getUserStateLocked(userId); 340 final SessionState sessionState = userState.sessionStateMap.get(sessionToken); 341 if (DEBUG) { 342 Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")"); 343 } 344 345 final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString()); 346 347 // Set up a callback to send the session token. 348 ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() { 349 @Override 350 public void onSessionCreated(ITvInputSession session) { 351 if (DEBUG) { 352 Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")"); 353 } 354 synchronized (mLock) { 355 sessionState.mSession = session; 356 if (session == null) { 357 removeSessionStateLocked(sessionToken, userId); 358 sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, 359 null, null, sessionState.mSeq, userId); 360 } else { 361 try { 362 session.asBinder().linkToDeath(sessionState, 0); 363 } catch (RemoteException e) { 364 Slog.e(TAG, "Session is already died."); 365 } 366 367 IBinder clientToken = sessionState.mClient.asBinder(); 368 ClientState clientState = userState.clientStateMap.get(clientToken); 369 if (clientState == null) { 370 clientState = createClientStateLocked(clientToken, userId); 371 } 372 clientState.mSessionTokens.add(sessionState.mSessionToken); 373 374 sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, 375 sessionToken, channels[0], sessionState.mSeq, userId); 376 } 377 channels[0].dispose(); 378 } 379 } 380 381 @Override 382 public void onVideoStreamChanged(int width, int height, boolean interlaced) { 383 synchronized (mLock) { 384 if (DEBUG) { 385 Slog.d(TAG, "onVideoStreamChanged(" + width + ", " + height + ")"); 386 } 387 if (sessionState.mSession == null || sessionState.mClient == null) { 388 return; 389 } 390 try { 391 sessionState.mClient.onVideoStreamChanged(width, height, interlaced, 392 sessionState.mSeq); 393 } catch (RemoteException e) { 394 Slog.e(TAG, "error in onVideoStreamChanged"); 395 } 396 } 397 } 398 399 @Override 400 public void onAudioStreamChanged(int channelCount) { 401 synchronized (mLock) { 402 if (DEBUG) { 403 Slog.d(TAG, "onAudioStreamChanged(" + channelCount + ")"); 404 } 405 if (sessionState.mSession == null || sessionState.mClient == null) { 406 return; 407 } 408 try { 409 sessionState.mClient.onAudioStreamChanged(channelCount, sessionState.mSeq); 410 } catch (RemoteException e) { 411 Slog.e(TAG, "error in onAudioStreamChanged"); 412 } 413 } 414 } 415 416 @Override 417 public void onClosedCaptionStreamChanged(boolean hasClosedCaption) { 418 synchronized (mLock) { 419 if (DEBUG) { 420 Slog.d(TAG, "onClosedCaptionStreamChanged(" + hasClosedCaption + ")"); 421 } 422 if (sessionState.mSession == null || sessionState.mClient == null) { 423 return; 424 } 425 try { 426 sessionState.mClient.onClosedCaptionStreamChanged(hasClosedCaption, 427 sessionState.mSeq); 428 } catch (RemoteException e) { 429 Slog.e(TAG, "error in onClosedCaptionStreamChanged"); 430 } 431 } 432 } 433 434 @Override 435 public void onSessionEvent(String eventType, Bundle eventArgs) { 436 synchronized (mLock) { 437 if (DEBUG) { 438 Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")"); 439 } 440 if (sessionState.mSession == null || sessionState.mClient == null) { 441 return; 442 } 443 try { 444 sessionState.mClient.onSessionEvent(eventType, eventArgs, 445 sessionState.mSeq); 446 } catch (RemoteException e) { 447 Slog.e(TAG, "error in onSessionEvent"); 448 } 449 } 450 } 451 }; 452 453 // Create a session. When failed, send a null token immediately. 454 try { 455 service.createSession(channels[1], callback); 456 } catch (RemoteException e) { 457 Slog.e(TAG, "error in createSession", e); 458 removeSessionStateLocked(sessionToken, userId); 459 sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null, 460 sessionState.mSeq, userId); 461 } 462 channels[1].dispose(); 463 } 464 465 private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId, 466 IBinder sessionToken, InputChannel channel, int seq, int userId) { 467 try { 468 client.onSessionCreated(inputId, sessionToken, channel, seq); 469 } catch (RemoteException exception) { 470 Slog.e(TAG, "error in onSessionCreated", exception); 471 } 472 } 473 474 private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { 475 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); 476 if (sessionState.mSession != null) { 477 try { 478 sessionState.mSession.release(); 479 } catch (RemoteException e) { 480 Slog.w(TAG, "session is already disapeared", e); 481 } 482 sessionState.mSession = null; 483 } 484 removeSessionStateLocked(sessionToken, userId); 485 } 486 487 private void removeSessionStateLocked(IBinder sessionToken, int userId) { 488 // Remove the session state from the global session state map of the current user. 489 UserState userState = getUserStateLocked(userId); 490 SessionState sessionState = userState.sessionStateMap.remove(sessionToken); 491 492 // Close the open log entry, if any. 493 if (sessionState.mLogUri != null) { 494 SomeArgs args = SomeArgs.obtain(); 495 args.arg1 = sessionState.mLogUri; 496 args.arg2 = System.currentTimeMillis(); 497 mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget(); 498 } 499 500 // Also remove the session token from the session token list of the current client and 501 // service. 502 ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder()); 503 if (clientState != null) { 504 clientState.mSessionTokens.remove(sessionToken); 505 if (clientState.isEmpty()) { 506 userState.clientStateMap.remove(sessionState.mClient.asBinder()); 507 } 508 } 509 510 ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId); 511 if (serviceState != null) { 512 serviceState.mSessionTokens.remove(sessionToken); 513 } 514 updateServiceConnectionLocked(sessionState.mInputId, userId); 515 } 516 517 private void unregisterCallbackInternalLocked(IBinder clientToken, String inputId, 518 int userId) { 519 UserState userState = getUserStateLocked(userId); 520 ClientState clientState = userState.clientStateMap.get(clientToken); 521 if (clientState != null) { 522 clientState.mInputIds.remove(inputId); 523 if (clientState.isEmpty()) { 524 userState.clientStateMap.remove(clientToken); 525 } 526 } 527 528 ServiceState serviceState = userState.serviceStateMap.get(inputId); 529 if (serviceState == null) { 530 return; 531 } 532 533 // Remove this client from the client list and unregister the callback. 534 serviceState.mClientTokens.remove(clientToken); 535 if (!serviceState.mClientTokens.isEmpty()) { 536 // We have other clients who want to keep the callback. Do this later. 537 return; 538 } 539 if (serviceState.mService == null || serviceState.mCallback == null) { 540 return; 541 } 542 try { 543 serviceState.mService.unregisterCallback(serviceState.mCallback); 544 } catch (RemoteException e) { 545 Slog.e(TAG, "error in unregisterCallback", e); 546 } finally { 547 serviceState.mCallback = null; 548 updateServiceConnectionLocked(inputId, userId); 549 } 550 } 551 552 private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) { 553 for (IBinder clientToken : serviceState.mClientTokens) { 554 try { 555 ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged( 556 serviceState.mTvInputInfo.getId(), serviceState.mAvailable); 557 } catch (RemoteException e) { 558 Slog.e(TAG, "error in onAvailabilityChanged", e); 559 } 560 } 561 } 562 563 private final class BinderService extends ITvInputManager.Stub { 564 @Override 565 public List<TvInputInfo> getTvInputList(int userId) { 566 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 567 Binder.getCallingUid(), userId, "getTvInputList"); 568 final long identity = Binder.clearCallingIdentity(); 569 try { 570 synchronized (mLock) { 571 UserState userState = getUserStateLocked(resolvedUserId); 572 return new ArrayList<TvInputInfo>(userState.inputMap.values()); 573 } 574 } finally { 575 Binder.restoreCallingIdentity(identity); 576 } 577 } 578 579 @Override 580 public boolean getAvailability(final ITvInputClient client, final String inputId, 581 int userId) { 582 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 583 Binder.getCallingUid(), userId, "getAvailability"); 584 final long identity = Binder.clearCallingIdentity(); 585 try { 586 synchronized (mLock) { 587 UserState userState = getUserStateLocked(resolvedUserId); 588 ServiceState serviceState = userState.serviceStateMap.get(inputId); 589 if (serviceState != null) { 590 // We already know the status of this input service. Return the cached 591 // status. 592 return serviceState.mAvailable; 593 } 594 } 595 } finally { 596 Binder.restoreCallingIdentity(identity); 597 } 598 return false; 599 } 600 601 @Override 602 public void registerCallback(final ITvInputClient client, final String inputId, 603 int userId) { 604 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 605 Binder.getCallingUid(), userId, "registerCallback"); 606 final long identity = Binder.clearCallingIdentity(); 607 try { 608 synchronized (mLock) { 609 // Create a new service callback and add it to the callback map of the current 610 // service. 611 UserState userState = getUserStateLocked(resolvedUserId); 612 ServiceState serviceState = userState.serviceStateMap.get(inputId); 613 if (serviceState == null) { 614 serviceState = new ServiceState( 615 userState.inputMap.get(inputId), resolvedUserId); 616 userState.serviceStateMap.put(inputId, serviceState); 617 } 618 IBinder clientToken = client.asBinder(); 619 if (!serviceState.mClientTokens.contains(clientToken)) { 620 serviceState.mClientTokens.add(clientToken); 621 } 622 623 ClientState clientState = userState.clientStateMap.get(clientToken); 624 if (clientState == null) { 625 clientState = createClientStateLocked(clientToken, resolvedUserId); 626 } 627 if (!clientState.mInputIds.contains(inputId)) { 628 clientState.mInputIds.add(inputId); 629 } 630 631 if (serviceState.mService != null) { 632 if (serviceState.mCallback != null) { 633 // We already handled. 634 return; 635 } 636 serviceState.mCallback = new ServiceCallback(resolvedUserId); 637 try { 638 serviceState.mService.registerCallback(serviceState.mCallback); 639 } catch (RemoteException e) { 640 Slog.e(TAG, "error in registerCallback", e); 641 } 642 } else { 643 updateServiceConnectionLocked(inputId, resolvedUserId); 644 } 645 } 646 } finally { 647 Binder.restoreCallingIdentity(identity); 648 } 649 } 650 651 @Override 652 public void unregisterCallback(ITvInputClient client, String inputId, int userId) { 653 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), 654 Binder.getCallingUid(), userId, "unregisterCallback"); 655 final long identity = Binder.clearCallingIdentity(); 656 try { 657 synchronized (mLock) { 658 unregisterCallbackInternalLocked(client.asBinder(), inputId, resolvedUserId); 659 } 660 } finally { 661 Binder.restoreCallingIdentity(identity); 662 } 663 } 664 665 @Override 666 public void createSession(final ITvInputClient client, final String inputId, 667 int seq, int userId) { 668 final int callingUid = Binder.getCallingUid(); 669 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 670 userId, "createSession"); 671 final long identity = Binder.clearCallingIdentity(); 672 try { 673 synchronized (mLock) { 674 UserState userState = getUserStateLocked(resolvedUserId); 675 ServiceState serviceState = userState.serviceStateMap.get(inputId); 676 if (serviceState == null) { 677 serviceState = new ServiceState( 678 userState.inputMap.get(inputId), resolvedUserId); 679 userState.serviceStateMap.put(inputId, serviceState); 680 } 681 // Send a null token immediately while reconnecting. 682 if (serviceState.mReconnecting == true) { 683 sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId); 684 return; 685 } 686 687 // Create a new session token and a session state. 688 IBinder sessionToken = new Binder(); 689 SessionState sessionState = new SessionState(sessionToken, inputId, client, 690 seq, callingUid, resolvedUserId); 691 692 // Add them to the global session state map of the current user. 693 userState.sessionStateMap.put(sessionToken, sessionState); 694 695 // Also, add them to the session state map of the current service. 696 serviceState.mSessionTokens.add(sessionToken); 697 698 if (serviceState.mService != null) { 699 createSessionInternalLocked(serviceState.mService, sessionToken, 700 resolvedUserId); 701 } else { 702 updateServiceConnectionLocked(inputId, resolvedUserId); 703 } 704 } 705 } finally { 706 Binder.restoreCallingIdentity(identity); 707 } 708 } 709 710 @Override 711 public void releaseSession(IBinder sessionToken, int userId) { 712 final int callingUid = Binder.getCallingUid(); 713 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 714 userId, "releaseSession"); 715 final long identity = Binder.clearCallingIdentity(); 716 try { 717 synchronized (mLock) { 718 releaseSessionLocked(sessionToken, callingUid, resolvedUserId); 719 } 720 } finally { 721 Binder.restoreCallingIdentity(identity); 722 } 723 } 724 725 @Override 726 public void setSurface(IBinder sessionToken, Surface surface, int userId) { 727 final int callingUid = Binder.getCallingUid(); 728 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 729 userId, "setSurface"); 730 final long identity = Binder.clearCallingIdentity(); 731 try { 732 synchronized (mLock) { 733 try { 734 getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface( 735 surface); 736 } catch (RemoteException e) { 737 Slog.e(TAG, "error in setSurface", e); 738 } 739 } 740 } finally { 741 if (surface != null) { 742 // surface is not used in TvInputManagerService. 743 surface.release(); 744 } 745 Binder.restoreCallingIdentity(identity); 746 } 747 } 748 749 @Override 750 public void setVolume(IBinder sessionToken, float volume, int userId) { 751 final int callingUid = Binder.getCallingUid(); 752 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 753 userId, "setVolume"); 754 final long identity = Binder.clearCallingIdentity(); 755 try { 756 synchronized (mLock) { 757 try { 758 getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume( 759 volume); 760 } catch (RemoteException e) { 761 Slog.e(TAG, "error in setVolume", e); 762 } 763 } 764 } finally { 765 Binder.restoreCallingIdentity(identity); 766 } 767 } 768 769 @Override 770 public void tune(IBinder sessionToken, final Uri channelUri, int userId) { 771 final int callingUid = Binder.getCallingUid(); 772 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 773 userId, "tune"); 774 final long identity = Binder.clearCallingIdentity(); 775 try { 776 synchronized (mLock) { 777 try { 778 getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri); 779 780 long currentTime = System.currentTimeMillis(); 781 long channelId = ContentUris.parseId(channelUri); 782 783 // Close the open log entry first, if any. 784 UserState userState = getUserStateLocked(resolvedUserId); 785 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 786 if (sessionState.mLogUri != null) { 787 SomeArgs args = SomeArgs.obtain(); 788 args.arg1 = sessionState.mLogUri; 789 args.arg2 = currentTime; 790 mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args) 791 .sendToTarget(); 792 } 793 794 // Create a log entry and fill it later. 795 ContentValues values = new ContentValues(); 796 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 797 currentTime); 798 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0); 799 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 800 801 sessionState.mLogUri = mContentResolver.insert( 802 TvContract.WatchedPrograms.CONTENT_URI, values); 803 SomeArgs args = SomeArgs.obtain(); 804 args.arg1 = sessionState.mLogUri; 805 args.arg2 = ContentUris.parseId(channelUri); 806 args.arg3 = currentTime; 807 mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget(); 808 } catch (RemoteException e) { 809 Slog.e(TAG, "error in tune", e); 810 return; 811 } 812 } 813 } finally { 814 Binder.restoreCallingIdentity(identity); 815 } 816 } 817 818 @Override 819 public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame, 820 int userId) { 821 final int callingUid = Binder.getCallingUid(); 822 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 823 userId, "createOverlayView"); 824 final long identity = Binder.clearCallingIdentity(); 825 try { 826 synchronized (mLock) { 827 try { 828 getSessionLocked(sessionToken, callingUid, resolvedUserId) 829 .createOverlayView(windowToken, frame); 830 } catch (RemoteException e) { 831 Slog.e(TAG, "error in createOverlayView", e); 832 } 833 } 834 } finally { 835 Binder.restoreCallingIdentity(identity); 836 } 837 } 838 839 @Override 840 public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) { 841 final int callingUid = Binder.getCallingUid(); 842 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 843 userId, "relayoutOverlayView"); 844 final long identity = Binder.clearCallingIdentity(); 845 try { 846 synchronized (mLock) { 847 try { 848 getSessionLocked(sessionToken, callingUid, resolvedUserId) 849 .relayoutOverlayView(frame); 850 } catch (RemoteException e) { 851 Slog.e(TAG, "error in relayoutOverlayView", e); 852 } 853 } 854 } finally { 855 Binder.restoreCallingIdentity(identity); 856 } 857 } 858 859 @Override 860 public void removeOverlayView(IBinder sessionToken, int userId) { 861 final int callingUid = Binder.getCallingUid(); 862 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 863 userId, "removeOverlayView"); 864 final long identity = Binder.clearCallingIdentity(); 865 try { 866 synchronized (mLock) { 867 try { 868 getSessionLocked(sessionToken, callingUid, resolvedUserId) 869 .removeOverlayView(); 870 } catch (RemoteException e) { 871 Slog.e(TAG, "error in removeOverlayView", e); 872 } 873 } 874 } finally { 875 Binder.restoreCallingIdentity(identity); 876 } 877 } 878 879 @Override 880 public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { 881 if (mContext.checkCallingPermission( 882 android.Manifest.permission.TV_INPUT_HARDWARE) 883 != PackageManager.PERMISSION_GRANTED) { 884 return null; 885 } 886 887 final long identity = Binder.clearCallingIdentity(); 888 try { 889 return mTvInputHardwareManager.getHardwareList(); 890 } finally { 891 Binder.restoreCallingIdentity(identity); 892 } 893 } 894 895 @Override 896 public ITvInputHardware acquireTvInputHardware(int deviceId, 897 ITvInputHardwareCallback callback, int userId) throws RemoteException { 898 if (mContext.checkCallingPermission( 899 android.Manifest.permission.TV_INPUT_HARDWARE) 900 != PackageManager.PERMISSION_GRANTED) { 901 return null; 902 } 903 904 final long identity = Binder.clearCallingIdentity(); 905 final int callingUid = Binder.getCallingUid(); 906 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 907 userId, "acquireTvInputHardware"); 908 try { 909 return mTvInputHardwareManager.acquireHardware( 910 deviceId, callback, callingUid, resolvedUserId); 911 } finally { 912 Binder.restoreCallingIdentity(identity); 913 } 914 } 915 916 @Override 917 public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) 918 throws RemoteException { 919 if (mContext.checkCallingPermission( 920 android.Manifest.permission.TV_INPUT_HARDWARE) 921 != PackageManager.PERMISSION_GRANTED) { 922 return; 923 } 924 925 final long identity = Binder.clearCallingIdentity(); 926 final int callingUid = Binder.getCallingUid(); 927 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, 928 userId, "releaseTvInputHardware"); 929 try { 930 mTvInputHardwareManager.releaseHardware( 931 deviceId, hardware, callingUid, resolvedUserId); 932 } finally { 933 Binder.restoreCallingIdentity(identity); 934 } 935 } 936 } 937 938 private static final class UserState { 939 // A mapping from the TV input id to its TvInputInfo. 940 private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>(); 941 942 // A mapping from the token of a client to its state. 943 private final Map<IBinder, ClientState> clientStateMap = 944 new HashMap<IBinder, ClientState>(); 945 946 // A mapping from the name of a TV input service to its state. 947 private final Map<String, ServiceState> serviceStateMap = 948 new HashMap<String, ServiceState>(); 949 950 // A mapping from the token of a TV input session to its state. 951 private final Map<IBinder, SessionState> sessionStateMap = 952 new HashMap<IBinder, SessionState>(); 953 } 954 955 private final class ClientState implements IBinder.DeathRecipient { 956 private final List<String> mInputIds = new ArrayList<String>(); 957 private final List<IBinder> mSessionTokens = new ArrayList<IBinder>(); 958 959 private IBinder mClientToken; 960 private final int mUserId; 961 962 ClientState(IBinder clientToken, int userId) { 963 mClientToken = clientToken; 964 mUserId = userId; 965 } 966 967 public boolean isEmpty() { 968 return mInputIds.isEmpty() && mSessionTokens.isEmpty(); 969 } 970 971 @Override 972 public void binderDied() { 973 synchronized (mLock) { 974 UserState userState = getUserStateLocked(mUserId); 975 // DO NOT remove the client state of clientStateMap in this method. It will be 976 // removed in releaseSessionLocked() or unregisterCallbackInternalLocked(). 977 ClientState clientState = userState.clientStateMap.get(mClientToken); 978 if (clientState != null) { 979 while (clientState.mSessionTokens.size() > 0) { 980 releaseSessionLocked( 981 clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId); 982 } 983 while (clientState.mInputIds.size() > 0) { 984 unregisterCallbackInternalLocked( 985 mClientToken, clientState.mInputIds.get(0), mUserId); 986 } 987 } 988 mClientToken = null; 989 } 990 } 991 } 992 993 private final class ServiceState { 994 private final List<IBinder> mClientTokens = new ArrayList<IBinder>(); 995 private final List<IBinder> mSessionTokens = new ArrayList<IBinder>(); 996 private final ServiceConnection mConnection; 997 private final TvInputInfo mTvInputInfo; 998 999 private ITvInputService mService; 1000 private ServiceCallback mCallback; 1001 private boolean mBound; 1002 private boolean mAvailable; 1003 private boolean mReconnecting; 1004 1005 private ServiceState(TvInputInfo inputInfo, int userId) { 1006 mTvInputInfo = inputInfo; 1007 mConnection = new InputServiceConnection(inputInfo, userId); 1008 } 1009 } 1010 1011 private final class SessionState implements IBinder.DeathRecipient { 1012 private final String mInputId; 1013 private final ITvInputClient mClient; 1014 private final int mSeq; 1015 private final int mCallingUid; 1016 private final int mUserId; 1017 private final IBinder mSessionToken; 1018 private ITvInputSession mSession; 1019 private Uri mLogUri; 1020 1021 private SessionState(IBinder sessionToken, String inputId, ITvInputClient client, int seq, 1022 int callingUid, int userId) { 1023 mSessionToken = sessionToken; 1024 mInputId = inputId; 1025 mClient = client; 1026 mSeq = seq; 1027 mCallingUid = callingUid; 1028 mUserId = userId; 1029 } 1030 1031 @Override 1032 public void binderDied() { 1033 synchronized (mLock) { 1034 mSession = null; 1035 if (mClient != null) { 1036 try { 1037 mClient.onSessionReleased(mSeq); 1038 } catch(RemoteException e) { 1039 Slog.e(TAG, "error in onSessionReleased", e); 1040 } 1041 } 1042 removeSessionStateLocked(mSessionToken, mUserId); 1043 } 1044 } 1045 } 1046 1047 private final class InputServiceConnection implements ServiceConnection { 1048 private final TvInputInfo mTvInputInfo; 1049 private final int mUserId; 1050 1051 private InputServiceConnection(TvInputInfo inputInfo, int userId) { 1052 mUserId = userId; 1053 mTvInputInfo = inputInfo; 1054 } 1055 1056 @Override 1057 public void onServiceConnected(ComponentName name, IBinder service) { 1058 if (DEBUG) { 1059 Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")"); 1060 } 1061 synchronized (mLock) { 1062 ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId); 1063 serviceState.mService = ITvInputService.Stub.asInterface(service); 1064 1065 // Register a callback, if we need to. 1066 if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) { 1067 serviceState.mCallback = new ServiceCallback(mUserId); 1068 try { 1069 serviceState.mService.registerCallback(serviceState.mCallback); 1070 } catch (RemoteException e) { 1071 Slog.e(TAG, "error in registerCallback", e); 1072 } 1073 } 1074 1075 // And create sessions, if any. 1076 for (IBinder sessionToken : serviceState.mSessionTokens) { 1077 createSessionInternalLocked(serviceState.mService, sessionToken, mUserId); 1078 } 1079 } 1080 } 1081 1082 @Override 1083 public void onServiceDisconnected(ComponentName name) { 1084 if (DEBUG) { 1085 Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")"); 1086 } 1087 if (!mTvInputInfo.getComponent().equals(name)) { 1088 throw new IllegalArgumentException("Mismatched ComponentName: " 1089 + mTvInputInfo.getComponent() + " (expected), " + name + " (actual)."); 1090 } 1091 synchronized (mLock) { 1092 UserState userState = getUserStateLocked(mUserId); 1093 ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId()); 1094 if (serviceState != null) { 1095 serviceState.mReconnecting = true; 1096 serviceState.mBound = false; 1097 serviceState.mService = null; 1098 serviceState.mCallback = null; 1099 1100 // Send null tokens for not finishing create session events. 1101 for (IBinder sessionToken : serviceState.mSessionTokens) { 1102 SessionState sessionState = userState.sessionStateMap.get(sessionToken); 1103 if (sessionState.mSession == null) { 1104 removeSessionStateLocked(sessionToken, sessionState.mUserId); 1105 sendSessionTokenToClientLocked(sessionState.mClient, 1106 sessionState.mInputId, null, null, sessionState.mSeq, 1107 sessionState.mUserId); 1108 } 1109 } 1110 1111 if (serviceState.mAvailable) { 1112 serviceState.mAvailable = false; 1113 broadcastServiceAvailabilityChangedLocked(serviceState); 1114 } 1115 updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId); 1116 } 1117 } 1118 } 1119 } 1120 1121 private final class ServiceCallback extends ITvInputServiceCallback.Stub { 1122 private final int mUserId; 1123 1124 ServiceCallback(int userId) { 1125 mUserId = userId; 1126 } 1127 1128 @Override 1129 public void onAvailabilityChanged(String inputId, boolean isAvailable) { 1130 if (DEBUG) { 1131 Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable=" 1132 + isAvailable + ")"); 1133 } 1134 synchronized (mLock) { 1135 ServiceState serviceState = getServiceStateLocked(inputId, mUserId); 1136 if (serviceState.mAvailable != isAvailable) { 1137 serviceState.mAvailable = isAvailable; 1138 broadcastServiceAvailabilityChangedLocked(serviceState); 1139 } 1140 } 1141 } 1142 } 1143 1144 private final class LogHandler extends Handler { 1145 private static final int MSG_OPEN_ENTRY = 1; 1146 private static final int MSG_UPDATE_ENTRY = 2; 1147 private static final int MSG_CLOSE_ENTRY = 3; 1148 1149 public LogHandler(Looper looper) { 1150 super(looper); 1151 } 1152 1153 @Override 1154 public void handleMessage(Message msg) { 1155 switch (msg.what) { 1156 case MSG_OPEN_ENTRY: { 1157 SomeArgs args = (SomeArgs) msg.obj; 1158 Uri uri = (Uri) args.arg1; 1159 long channelId = (long) args.arg2; 1160 long time = (long) args.arg3; 1161 onOpenEntry(uri, channelId, time); 1162 args.recycle(); 1163 return; 1164 } 1165 case MSG_UPDATE_ENTRY: { 1166 SomeArgs args = (SomeArgs) msg.obj; 1167 Uri uri = (Uri) args.arg1; 1168 long channelId = (long) args.arg2; 1169 long time = (long) args.arg3; 1170 onUpdateEntry(uri, channelId, time); 1171 args.recycle(); 1172 return; 1173 } 1174 case MSG_CLOSE_ENTRY: { 1175 SomeArgs args = (SomeArgs) msg.obj; 1176 Uri uri = (Uri) args.arg1; 1177 long time = (long) args.arg2; 1178 onCloseEntry(uri, time); 1179 args.recycle(); 1180 return; 1181 } 1182 default: { 1183 Slog.w(TAG, "Unhandled message code: " + msg.what); 1184 return; 1185 } 1186 } 1187 } 1188 1189 private void onOpenEntry(Uri uri, long channelId, long watchStarttime) { 1190 String[] projection = { 1191 TvContract.Programs.COLUMN_TITLE, 1192 TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, 1193 TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, 1194 TvContract.Programs.COLUMN_SHORT_DESCRIPTION 1195 }; 1196 String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND " 1197 + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND " 1198 + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS + ">?"; 1199 String[] selectionArgs = { 1200 String.valueOf(channelId), 1201 String.valueOf(watchStarttime), 1202 String.valueOf(watchStarttime) 1203 }; 1204 String sortOrder = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC"; 1205 Cursor cursor = null; 1206 try { 1207 cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection, 1208 selection, selectionArgs, sortOrder); 1209 if (cursor != null && cursor.moveToNext()) { 1210 ContentValues values = new ContentValues(); 1211 values.put(TvContract.WatchedPrograms.COLUMN_TITLE, cursor.getString(0)); 1212 values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, 1213 cursor.getLong(1)); 1214 long endTime = cursor.getLong(2); 1215 values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); 1216 values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, cursor.getString(3)); 1217 mContentResolver.update(uri, values, null, null); 1218 1219 // Schedule an update when the current program ends. 1220 SomeArgs args = SomeArgs.obtain(); 1221 args.arg1 = uri; 1222 args.arg2 = channelId; 1223 args.arg3 = endTime; 1224 Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args); 1225 sendMessageDelayed(msg, endTime - System.currentTimeMillis()); 1226 } 1227 } finally { 1228 if (cursor != null) { 1229 cursor.close(); 1230 } 1231 } 1232 } 1233 1234 private void onUpdateEntry(Uri uri, long channelId, long time) { 1235 String[] projection = { 1236 TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 1237 TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 1238 TvContract.WatchedPrograms.COLUMN_TITLE, 1239 TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, 1240 TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, 1241 TvContract.WatchedPrograms.COLUMN_DESCRIPTION 1242 }; 1243 Cursor cursor = null; 1244 try { 1245 cursor = mContentResolver.query(uri, projection, null, null, null); 1246 if (cursor != null && cursor.moveToNext()) { 1247 long watchStartTime = cursor.getLong(0); 1248 long watchEndTime = cursor.getLong(1); 1249 String title = cursor.getString(2); 1250 long startTime = cursor.getLong(3); 1251 long endTime = cursor.getLong(4); 1252 String description = cursor.getString(5); 1253 1254 // Do nothing if the current log entry is already closed. 1255 if (watchEndTime > 0) { 1256 return; 1257 } 1258 1259 // The current program has just ended. Create a (complete) log entry off the 1260 // current entry. 1261 ContentValues values = new ContentValues(); 1262 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 1263 watchStartTime); 1264 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, time); 1265 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 1266 values.put(TvContract.WatchedPrograms.COLUMN_TITLE, title); 1267 values.put(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime); 1268 values.put(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); 1269 values.put(TvContract.WatchedPrograms.COLUMN_DESCRIPTION, description); 1270 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values); 1271 } 1272 } finally { 1273 if (cursor != null) { 1274 cursor.close(); 1275 } 1276 } 1277 // Re-open the current log entry with the next program information. 1278 onOpenEntry(uri, channelId, time); 1279 } 1280 1281 private void onCloseEntry(Uri uri, long watchEndTime) { 1282 ContentValues values = new ContentValues(); 1283 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, watchEndTime); 1284 mContentResolver.update(uri, values, null, null); 1285 } 1286 } 1287} 1288