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