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