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 (DEBUG) {
   1222                 Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")");
   1223             }
   1224             final int callingUid = Binder.getCallingUid();
   1225             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1226                     userId, "setMainSession");
   1227             final long identity = Binder.clearCallingIdentity();
   1228             try {
   1229                 synchronized (mLock) {
   1230                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
   1231                     if (userState.mainSessionToken == sessionToken) {
   1232                         return;
   1233                     }
   1234                     if (DEBUG) {
   1235                         Slog.d(TAG, "mainSessionToken=" + sessionToken);
   1236                     }
   1237                     IBinder oldMainSessionToken = userState.mainSessionToken;
   1238                     userState.mainSessionToken = sessionToken;
   1239 
   1240                     // Inform the new main session first.
   1241                     // See {@link TvInputService.Session#onSetMain}.
   1242                     if (sessionToken != null) {
   1243                         setMainLocked(sessionToken, true, callingUid, userId);
   1244                     }
   1245                     if (oldMainSessionToken != null) {
   1246                         setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId);
   1247                     }
   1248                 }
   1249             } finally {
   1250                 Binder.restoreCallingIdentity(identity);
   1251             }
   1252         }
   1253 
   1254         @Override
   1255         public void setSurface(IBinder sessionToken, Surface surface, int userId) {
   1256             final int callingUid = Binder.getCallingUid();
   1257             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1258                     userId, "setSurface");
   1259             final long identity = Binder.clearCallingIdentity();
   1260             try {
   1261                 synchronized (mLock) {
   1262                     try {
   1263                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
   1264                                 resolvedUserId);
   1265                         if (sessionState.hardwareSessionToken == null) {
   1266                             getSessionLocked(sessionState).setSurface(surface);
   1267                         } else {
   1268                             getSessionLocked(sessionState.hardwareSessionToken,
   1269                                     Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
   1270                         }
   1271                     } catch (RemoteException | SessionNotFoundException e) {
   1272                         Slog.e(TAG, "error in setSurface", e);
   1273                     }
   1274                 }
   1275             } finally {
   1276                 if (surface != null) {
   1277                     // surface is not used in TvInputManagerService.
   1278                     surface.release();
   1279                 }
   1280                 Binder.restoreCallingIdentity(identity);
   1281             }
   1282         }
   1283 
   1284         @Override
   1285         public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
   1286                 int height, int userId) {
   1287             final int callingUid = Binder.getCallingUid();
   1288             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1289                     userId, "dispatchSurfaceChanged");
   1290             final long identity = Binder.clearCallingIdentity();
   1291             try {
   1292                 synchronized (mLock) {
   1293                     try {
   1294                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
   1295                                 resolvedUserId);
   1296                         getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
   1297                                 height);
   1298                         if (sessionState.hardwareSessionToken != null) {
   1299                             getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
   1300                                     resolvedUserId).dispatchSurfaceChanged(format, width, height);
   1301                         }
   1302                     } catch (RemoteException | SessionNotFoundException e) {
   1303                         Slog.e(TAG, "error in dispatchSurfaceChanged", e);
   1304                     }
   1305                 }
   1306             } finally {
   1307                 Binder.restoreCallingIdentity(identity);
   1308             }
   1309         }
   1310 
   1311         @Override
   1312         public void setVolume(IBinder sessionToken, float volume, int userId) {
   1313             final float REMOTE_VOLUME_ON = 1.0f;
   1314             final float REMOTE_VOLUME_OFF = 0f;
   1315             final int callingUid = Binder.getCallingUid();
   1316             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1317                     userId, "setVolume");
   1318             final long identity = Binder.clearCallingIdentity();
   1319             try {
   1320                 synchronized (mLock) {
   1321                     try {
   1322                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
   1323                                 resolvedUserId);
   1324                         getSessionLocked(sessionState).setVolume(volume);
   1325                         if (sessionState.hardwareSessionToken != null) {
   1326                             // Here, we let the hardware session know only whether volume is on or
   1327                             // off to prevent that the volume is controlled in the both side.
   1328                             getSessionLocked(sessionState.hardwareSessionToken,
   1329                                     Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
   1330                                             ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
   1331                         }
   1332                     } catch (RemoteException | SessionNotFoundException e) {
   1333                         Slog.e(TAG, "error in setVolume", e);
   1334                     }
   1335                 }
   1336             } finally {
   1337                 Binder.restoreCallingIdentity(identity);
   1338             }
   1339         }
   1340 
   1341         @Override
   1342         public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
   1343             final int callingUid = Binder.getCallingUid();
   1344             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1345                     userId, "tune");
   1346             final long identity = Binder.clearCallingIdentity();
   1347             try {
   1348                 synchronized (mLock) {
   1349                     try {
   1350                         getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
   1351                                 channelUri, params);
   1352                         if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
   1353                             // Do not log the watch history for passthrough inputs.
   1354                             return;
   1355                         }
   1356 
   1357                         UserState userState = getOrCreateUserStateLocked(resolvedUserId);
   1358                         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
   1359                         if (sessionState.isRecordingSession) {
   1360                             return;
   1361                         }
   1362 
   1363                         // Log the start of watch.
   1364                         SomeArgs args = SomeArgs.obtain();
   1365                         args.arg1 = sessionState.componentName.getPackageName();
   1366                         args.arg2 = System.currentTimeMillis();
   1367                         args.arg3 = ContentUris.parseId(channelUri);
   1368                         args.arg4 = params;
   1369                         args.arg5 = sessionToken;
   1370                         mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args)
   1371                                 .sendToTarget();
   1372                     } catch (RemoteException | SessionNotFoundException e) {
   1373                         Slog.e(TAG, "error in tune", e);
   1374                     }
   1375                 }
   1376             } finally {
   1377                 Binder.restoreCallingIdentity(identity);
   1378             }
   1379         }
   1380 
   1381         @Override
   1382         public void unblockContent(
   1383                 IBinder sessionToken, String unblockedRating, int userId) {
   1384             ensureParentalControlsPermission();
   1385             final int callingUid = Binder.getCallingUid();
   1386             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1387                     userId, "unblockContent");
   1388             final long identity = Binder.clearCallingIdentity();
   1389             try {
   1390                 synchronized (mLock) {
   1391                     try {
   1392                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1393                                 .unblockContent(unblockedRating);
   1394                     } catch (RemoteException | SessionNotFoundException e) {
   1395                         Slog.e(TAG, "error in unblockContent", e);
   1396                     }
   1397                 }
   1398             } finally {
   1399                 Binder.restoreCallingIdentity(identity);
   1400             }
   1401         }
   1402 
   1403         @Override
   1404         public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
   1405             final int callingUid = Binder.getCallingUid();
   1406             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1407                     userId, "setCaptionEnabled");
   1408             final long identity = Binder.clearCallingIdentity();
   1409             try {
   1410                 synchronized (mLock) {
   1411                     try {
   1412                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1413                                 .setCaptionEnabled(enabled);
   1414                     } catch (RemoteException | SessionNotFoundException e) {
   1415                         Slog.e(TAG, "error in setCaptionEnabled", e);
   1416                     }
   1417                 }
   1418             } finally {
   1419                 Binder.restoreCallingIdentity(identity);
   1420             }
   1421         }
   1422 
   1423         @Override
   1424         public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
   1425             final int callingUid = Binder.getCallingUid();
   1426             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1427                     userId, "selectTrack");
   1428             final long identity = Binder.clearCallingIdentity();
   1429             try {
   1430                 synchronized (mLock) {
   1431                     try {
   1432                         getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
   1433                                 type, trackId);
   1434                     } catch (RemoteException | SessionNotFoundException e) {
   1435                         Slog.e(TAG, "error in selectTrack", e);
   1436                     }
   1437                 }
   1438             } finally {
   1439                 Binder.restoreCallingIdentity(identity);
   1440             }
   1441         }
   1442 
   1443         @Override
   1444         public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
   1445                 int userId) {
   1446             final int callingUid = Binder.getCallingUid();
   1447             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1448                     userId, "sendAppPrivateCommand");
   1449             final long identity = Binder.clearCallingIdentity();
   1450             try {
   1451                 synchronized (mLock) {
   1452                     try {
   1453                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1454                                 .appPrivateCommand(command, data);
   1455                     } catch (RemoteException | SessionNotFoundException e) {
   1456                         Slog.e(TAG, "error in appPrivateCommand", e);
   1457                     }
   1458                 }
   1459             } finally {
   1460                 Binder.restoreCallingIdentity(identity);
   1461             }
   1462         }
   1463 
   1464         @Override
   1465         public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
   1466                 int userId) {
   1467             final int callingUid = Binder.getCallingUid();
   1468             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1469                     userId, "createOverlayView");
   1470             final long identity = Binder.clearCallingIdentity();
   1471             try {
   1472                 synchronized (mLock) {
   1473                     try {
   1474                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1475                                 .createOverlayView(windowToken, frame);
   1476                     } catch (RemoteException | SessionNotFoundException e) {
   1477                         Slog.e(TAG, "error in createOverlayView", e);
   1478                     }
   1479                 }
   1480             } finally {
   1481                 Binder.restoreCallingIdentity(identity);
   1482             }
   1483         }
   1484 
   1485         @Override
   1486         public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
   1487             final int callingUid = Binder.getCallingUid();
   1488             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1489                     userId, "relayoutOverlayView");
   1490             final long identity = Binder.clearCallingIdentity();
   1491             try {
   1492                 synchronized (mLock) {
   1493                     try {
   1494                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1495                                 .relayoutOverlayView(frame);
   1496                     } catch (RemoteException | SessionNotFoundException e) {
   1497                         Slog.e(TAG, "error in relayoutOverlayView", e);
   1498                     }
   1499                 }
   1500             } finally {
   1501                 Binder.restoreCallingIdentity(identity);
   1502             }
   1503         }
   1504 
   1505         @Override
   1506         public void removeOverlayView(IBinder sessionToken, int userId) {
   1507             final int callingUid = Binder.getCallingUid();
   1508             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1509                     userId, "removeOverlayView");
   1510             final long identity = Binder.clearCallingIdentity();
   1511             try {
   1512                 synchronized (mLock) {
   1513                     try {
   1514                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1515                                 .removeOverlayView();
   1516                     } catch (RemoteException | SessionNotFoundException e) {
   1517                         Slog.e(TAG, "error in removeOverlayView", e);
   1518                     }
   1519                 }
   1520             } finally {
   1521                 Binder.restoreCallingIdentity(identity);
   1522             }
   1523         }
   1524 
   1525         @Override
   1526         public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
   1527             final int callingUid = Binder.getCallingUid();
   1528             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1529                     userId, "timeShiftPlay");
   1530             final long identity = Binder.clearCallingIdentity();
   1531             try {
   1532                 synchronized (mLock) {
   1533                     try {
   1534                         getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay(
   1535                                 recordedProgramUri);
   1536                     } catch (RemoteException | SessionNotFoundException e) {
   1537                         Slog.e(TAG, "error in timeShiftPlay", e);
   1538                     }
   1539                 }
   1540             } finally {
   1541                 Binder.restoreCallingIdentity(identity);
   1542             }
   1543         }
   1544 
   1545         @Override
   1546         public void timeShiftPause(IBinder sessionToken, int userId) {
   1547             final int callingUid = Binder.getCallingUid();
   1548             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1549                     userId, "timeShiftPause");
   1550             final long identity = Binder.clearCallingIdentity();
   1551             try {
   1552                 synchronized (mLock) {
   1553                     try {
   1554                         getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause();
   1555                     } catch (RemoteException | SessionNotFoundException e) {
   1556                         Slog.e(TAG, "error in timeShiftPause", e);
   1557                     }
   1558                 }
   1559             } finally {
   1560                 Binder.restoreCallingIdentity(identity);
   1561             }
   1562         }
   1563 
   1564         @Override
   1565         public void timeShiftResume(IBinder sessionToken, int userId) {
   1566             final int callingUid = Binder.getCallingUid();
   1567             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1568                     userId, "timeShiftResume");
   1569             final long identity = Binder.clearCallingIdentity();
   1570             try {
   1571                 synchronized (mLock) {
   1572                     try {
   1573                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1574                                 .timeShiftResume();
   1575                     } catch (RemoteException | SessionNotFoundException e) {
   1576                         Slog.e(TAG, "error in timeShiftResume", e);
   1577                     }
   1578                 }
   1579             } finally {
   1580                 Binder.restoreCallingIdentity(identity);
   1581             }
   1582         }
   1583 
   1584         @Override
   1585         public void timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId) {
   1586             final int callingUid = Binder.getCallingUid();
   1587             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1588                     userId, "timeShiftSeekTo");
   1589             final long identity = Binder.clearCallingIdentity();
   1590             try {
   1591                 synchronized (mLock) {
   1592                     try {
   1593                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1594                                 .timeShiftSeekTo(timeMs);
   1595                     } catch (RemoteException | SessionNotFoundException e) {
   1596                         Slog.e(TAG, "error in timeShiftSeekTo", e);
   1597                     }
   1598                 }
   1599             } finally {
   1600                 Binder.restoreCallingIdentity(identity);
   1601             }
   1602         }
   1603 
   1604         @Override
   1605         public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params,
   1606                 int userId) {
   1607             final int callingUid = Binder.getCallingUid();
   1608             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1609                     userId, "timeShiftSetPlaybackParams");
   1610             final long identity = Binder.clearCallingIdentity();
   1611             try {
   1612                 synchronized (mLock) {
   1613                     try {
   1614                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1615                                 .timeShiftSetPlaybackParams(params);
   1616                     } catch (RemoteException | SessionNotFoundException e) {
   1617                         Slog.e(TAG, "error in timeShiftSetPlaybackParams", e);
   1618                     }
   1619                 }
   1620             } finally {
   1621                 Binder.restoreCallingIdentity(identity);
   1622             }
   1623         }
   1624 
   1625         @Override
   1626         public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable,
   1627                 int userId) {
   1628             final int callingUid = Binder.getCallingUid();
   1629             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1630                     userId, "timeShiftEnablePositionTracking");
   1631             final long identity = Binder.clearCallingIdentity();
   1632             try {
   1633                 synchronized (mLock) {
   1634                     try {
   1635                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
   1636                                 .timeShiftEnablePositionTracking(enable);
   1637                     } catch (RemoteException | SessionNotFoundException e) {
   1638                         Slog.e(TAG, "error in timeShiftEnablePositionTracking", e);
   1639                     }
   1640                 }
   1641             } finally {
   1642                 Binder.restoreCallingIdentity(identity);
   1643             }
   1644         }
   1645 
   1646         @Override
   1647         public void startRecording(IBinder sessionToken, @Nullable Uri programUri, int userId) {
   1648             final int callingUid = Binder.getCallingUid();
   1649             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1650                     userId, "startRecording");
   1651             final long identity = Binder.clearCallingIdentity();
   1652             try {
   1653                 synchronized (mLock) {
   1654                     try {
   1655                         getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording(
   1656                                 programUri);
   1657                     } catch (RemoteException | SessionNotFoundException e) {
   1658                         Slog.e(TAG, "error in startRecording", e);
   1659                     }
   1660                 }
   1661             } finally {
   1662                 Binder.restoreCallingIdentity(identity);
   1663             }
   1664         }
   1665 
   1666         @Override
   1667         public void stopRecording(IBinder sessionToken, int userId) {
   1668             final int callingUid = Binder.getCallingUid();
   1669             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1670                     userId, "stopRecording");
   1671             final long identity = Binder.clearCallingIdentity();
   1672             try {
   1673                 synchronized (mLock) {
   1674                     try {
   1675                         getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording();
   1676                     } catch (RemoteException | SessionNotFoundException e) {
   1677                         Slog.e(TAG, "error in stopRecording", e);
   1678                     }
   1679                 }
   1680             } finally {
   1681                 Binder.restoreCallingIdentity(identity);
   1682             }
   1683         }
   1684 
   1685         @Override
   1686         public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
   1687             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
   1688                     != PackageManager.PERMISSION_GRANTED) {
   1689                 return null;
   1690             }
   1691 
   1692             final long identity = Binder.clearCallingIdentity();
   1693             try {
   1694                 return mTvInputHardwareManager.getHardwareList();
   1695             } finally {
   1696                 Binder.restoreCallingIdentity(identity);
   1697             }
   1698         }
   1699 
   1700         @Override
   1701         public ITvInputHardware acquireTvInputHardware(int deviceId,
   1702                 ITvInputHardwareCallback callback, TvInputInfo info, int userId)
   1703                 throws RemoteException {
   1704             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
   1705                     != PackageManager.PERMISSION_GRANTED) {
   1706                 return null;
   1707             }
   1708 
   1709             final long identity = Binder.clearCallingIdentity();
   1710             final int callingUid = Binder.getCallingUid();
   1711             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1712                     userId, "acquireTvInputHardware");
   1713             try {
   1714                 return mTvInputHardwareManager.acquireHardware(
   1715                         deviceId, callback, info, callingUid, resolvedUserId);
   1716             } finally {
   1717                 Binder.restoreCallingIdentity(identity);
   1718             }
   1719         }
   1720 
   1721         @Override
   1722         public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
   1723                 throws RemoteException {
   1724             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
   1725                     != PackageManager.PERMISSION_GRANTED) {
   1726                 return;
   1727             }
   1728 
   1729             final long identity = Binder.clearCallingIdentity();
   1730             final int callingUid = Binder.getCallingUid();
   1731             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1732                     userId, "releaseTvInputHardware");
   1733             try {
   1734                 mTvInputHardwareManager.releaseHardware(
   1735                         deviceId, hardware, callingUid, resolvedUserId);
   1736             } finally {
   1737                 Binder.restoreCallingIdentity(identity);
   1738             }
   1739         }
   1740 
   1741         @Override
   1742         public List<DvbDeviceInfo> getDvbDeviceList() throws RemoteException {
   1743             if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
   1744                     != PackageManager.PERMISSION_GRANTED) {
   1745                 throw new SecurityException("Requires DVB_DEVICE permission");
   1746             }
   1747 
   1748             final long identity = Binder.clearCallingIdentity();
   1749             try {
   1750                 // Pattern1: /dev/dvb%d.frontend%d
   1751                 ArrayList<DvbDeviceInfo> deviceInfosFromPattern1 = new ArrayList<>();
   1752                 File devDirectory = new File("/dev");
   1753                 boolean dvbDirectoryFound = false;
   1754                 for (String fileName : devDirectory.list()) {
   1755                     Matcher matcher = sFrontEndDevicePattern.matcher(fileName);
   1756                     if (matcher.find()) {
   1757                         int adapterId = Integer.parseInt(matcher.group(1));
   1758                         int deviceId = Integer.parseInt(matcher.group(2));
   1759                         deviceInfosFromPattern1.add(new DvbDeviceInfo(adapterId, deviceId));
   1760                     }
   1761                     if (TextUtils.equals("dvb", fileName)) {
   1762                         dvbDirectoryFound = true;
   1763                     }
   1764                 }
   1765                 if (!dvbDirectoryFound) {
   1766                     return Collections.unmodifiableList(deviceInfosFromPattern1);
   1767                 }
   1768                 File dvbDirectory = new File(DVB_DIRECTORY);
   1769                 // Pattern2: /dev/dvb/adapter%d/frontend%d
   1770                 ArrayList<DvbDeviceInfo> deviceInfosFromPattern2 = new ArrayList<>();
   1771                 for (String fileNameInDvb : dvbDirectory.list()) {
   1772                     Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
   1773                     if (adapterMatcher.find()) {
   1774                         int adapterId = Integer.parseInt(adapterMatcher.group(1));
   1775                         File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
   1776                         for (String fileNameInAdapter : adapterDirectory.list()) {
   1777                             Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
   1778                                     fileNameInAdapter);
   1779                             if (frontendMatcher.find()) {
   1780                                 int deviceId = Integer.parseInt(frontendMatcher.group(1));
   1781                                 deviceInfosFromPattern2.add(
   1782                                         new DvbDeviceInfo(adapterId, deviceId));
   1783                             }
   1784                         }
   1785                     }
   1786                 }
   1787                 return deviceInfosFromPattern2.isEmpty()
   1788                         ? Collections.unmodifiableList(deviceInfosFromPattern1)
   1789                         : Collections.unmodifiableList(deviceInfosFromPattern2);
   1790             } finally {
   1791                 Binder.restoreCallingIdentity(identity);
   1792             }
   1793         }
   1794 
   1795         @Override
   1796         public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device)
   1797                 throws RemoteException {
   1798             if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
   1799                     != PackageManager.PERMISSION_GRANTED) {
   1800                 throw new SecurityException("Requires DVB_DEVICE permission");
   1801             }
   1802 
   1803             File devDirectory = new File("/dev");
   1804             boolean dvbDeviceFound = false;
   1805             for (String fileName : devDirectory.list()) {
   1806                 if (TextUtils.equals("dvb", fileName)) {
   1807                     File dvbDirectory = new File(DVB_DIRECTORY);
   1808                     for (String fileNameInDvb : dvbDirectory.list()) {
   1809                         Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
   1810                         if (adapterMatcher.find()) {
   1811                             File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
   1812                             for (String fileNameInAdapter : adapterDirectory.list()) {
   1813                                 Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
   1814                                         fileNameInAdapter);
   1815                                 if (frontendMatcher.find()) {
   1816                                     dvbDeviceFound = true;
   1817                                     break;
   1818                                 }
   1819                             }
   1820                         }
   1821                         if (dvbDeviceFound) {
   1822                             break;
   1823                         }
   1824                     }
   1825                 }
   1826                 if (dvbDeviceFound) {
   1827                     break;
   1828                 }
   1829             }
   1830 
   1831             final long identity = Binder.clearCallingIdentity();
   1832             try {
   1833                 String deviceFileName;
   1834                 switch (device) {
   1835                     case TvInputManager.DVB_DEVICE_DEMUX:
   1836                         deviceFileName = String.format(dvbDeviceFound
   1837                                 ? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d",
   1838                                 info.getAdapterId(), info.getDeviceId());
   1839                         break;
   1840                     case TvInputManager.DVB_DEVICE_DVR:
   1841                         deviceFileName = String.format(dvbDeviceFound
   1842                                 ? "/dev/dvb/adapter%d/dvr%d" : "/dev/dvb%d.dvr%d",
   1843                                 info.getAdapterId(), info.getDeviceId());
   1844                         break;
   1845                     case TvInputManager.DVB_DEVICE_FRONTEND:
   1846                         deviceFileName = String.format(dvbDeviceFound
   1847                                 ? "/dev/dvb/adapter%d/frontend%d" : "/dev/dvb%d.frontend%d",
   1848                                 info.getAdapterId(), info.getDeviceId());
   1849                         break;
   1850                     default:
   1851                         throw new IllegalArgumentException("Invalid DVB device: " + device);
   1852                 }
   1853                 try {
   1854                     // The DVB frontend device only needs to be opened in read/write mode, which
   1855                     // allows performing tuning operations. The DVB demux and DVR device are enough
   1856                     // to be opened in read only mode.
   1857                     return ParcelFileDescriptor.open(new File(deviceFileName),
   1858                             TvInputManager.DVB_DEVICE_FRONTEND == device
   1859                                     ? ParcelFileDescriptor.MODE_READ_WRITE
   1860                                     : ParcelFileDescriptor.MODE_READ_ONLY);
   1861                 } catch (FileNotFoundException e) {
   1862                     return null;
   1863                 }
   1864             } finally {
   1865                 Binder.restoreCallingIdentity(identity);
   1866             }
   1867         }
   1868 
   1869         @Override
   1870         public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
   1871                 throws RemoteException {
   1872             if (mContext.checkCallingPermission(
   1873                     android.Manifest.permission.CAPTURE_TV_INPUT)
   1874                     != PackageManager.PERMISSION_GRANTED) {
   1875                 throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
   1876             }
   1877 
   1878             final long identity = Binder.clearCallingIdentity();
   1879             final int callingUid = Binder.getCallingUid();
   1880             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1881                     userId, "getAvailableTvStreamConfigList");
   1882             try {
   1883                 return mTvInputHardwareManager.getAvailableTvStreamConfigList(
   1884                         inputId, callingUid, resolvedUserId);
   1885             } finally {
   1886                 Binder.restoreCallingIdentity(identity);
   1887             }
   1888         }
   1889 
   1890         @Override
   1891         public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
   1892                 int userId)
   1893                 throws RemoteException {
   1894             if (mContext.checkCallingPermission(
   1895                     android.Manifest.permission.CAPTURE_TV_INPUT)
   1896                     != PackageManager.PERMISSION_GRANTED) {
   1897                 throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
   1898             }
   1899 
   1900             final long identity = Binder.clearCallingIdentity();
   1901             final int callingUid = Binder.getCallingUid();
   1902             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1903                     userId, "captureFrame");
   1904             try {
   1905                 String hardwareInputId = null;
   1906                 synchronized (mLock) {
   1907                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
   1908                     if (userState.inputMap.get(inputId) == null) {
   1909                         Slog.e(TAG, "input not found for " + inputId);
   1910                         return false;
   1911                     }
   1912                     for (SessionState sessionState : userState.sessionStateMap.values()) {
   1913                         if (sessionState.inputId.equals(inputId)
   1914                                 && sessionState.hardwareSessionToken != null) {
   1915                             hardwareInputId = userState.sessionStateMap.get(
   1916                                     sessionState.hardwareSessionToken).inputId;
   1917                             break;
   1918                         }
   1919                     }
   1920                 }
   1921                 return mTvInputHardwareManager.captureFrame(
   1922                         (hardwareInputId != null) ? hardwareInputId : inputId,
   1923                         surface, config, callingUid, resolvedUserId);
   1924             } finally {
   1925                 Binder.restoreCallingIdentity(identity);
   1926             }
   1927         }
   1928 
   1929         @Override
   1930         public boolean isSingleSessionActive(int userId) throws RemoteException {
   1931             final long identity = Binder.clearCallingIdentity();
   1932             final int callingUid = Binder.getCallingUid();
   1933             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1934                     userId, "isSingleSessionActive");
   1935             try {
   1936                 synchronized (mLock) {
   1937                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
   1938                     if (userState.sessionStateMap.size() == 1) {
   1939                         return true;
   1940                     } else if (userState.sessionStateMap.size() == 2) {
   1941                         SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
   1942                                 new SessionState[2]);
   1943                         // Check if there is a wrapper input.
   1944                         if (sessionStates[0].hardwareSessionToken != null
   1945                                 || sessionStates[1].hardwareSessionToken != null) {
   1946                             return true;
   1947                         }
   1948                     }
   1949                     return false;
   1950                 }
   1951             } finally {
   1952                 Binder.restoreCallingIdentity(identity);
   1953             }
   1954         }
   1955 
   1956         @Override
   1957         public void requestChannelBrowsable(Uri channelUri, int userId)
   1958                 throws RemoteException {
   1959             final String callingPackageName = getCallingPackageName();
   1960             final long identity = Binder.clearCallingIdentity();
   1961             final int callingUid = Binder.getCallingUid();
   1962             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
   1963                 userId, "requestChannelBrowsable");
   1964             try {
   1965                 Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
   1966                 List<ResolveInfo> list = getContext().getPackageManager()
   1967                     .queryBroadcastReceivers(intent, 0);
   1968                 if (list != null) {
   1969                     for (ResolveInfo info : list) {
   1970                         String receiverPackageName = info.activityInfo.packageName;
   1971                         intent.putExtra(TvContract.EXTRA_CHANNEL_ID, ContentUris.parseId(
   1972                                 channelUri));
   1973                         intent.putExtra(TvContract.EXTRA_PACKAGE_NAME, callingPackageName);
   1974                         intent.setPackage(receiverPackageName);
   1975                         getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
   1976                     }
   1977                 }
   1978             } finally {
   1979                 Binder.restoreCallingIdentity(identity);
   1980             }
   1981         }
   1982 
   1983         @Override
   1984         @SuppressWarnings("resource")
   1985         protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
   1986             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
   1987             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
   1988 
   1989             synchronized (mLock) {
   1990                 pw.println("User Ids (Current user: " + mCurrentUserId + "):");
   1991                 pw.increaseIndent();
   1992                 for (int i = 0; i < mUserStates.size(); i++) {
   1993                     int userId = mUserStates.keyAt(i);
   1994                     pw.println(Integer.valueOf(userId));
   1995                 }
   1996                 pw.decreaseIndent();
   1997 
   1998                 for (int i = 0; i < mUserStates.size(); i++) {
   1999                     int userId = mUserStates.keyAt(i);
   2000                     UserState userState = getOrCreateUserStateLocked(userId);
   2001                     pw.println("UserState (" + userId + "):");
   2002                     pw.increaseIndent();
   2003 
   2004                     pw.println("inputMap: inputId -> TvInputState");
   2005                     pw.increaseIndent();
   2006                     for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) {
   2007                         pw.println(entry.getKey() + ": " + entry.getValue());
   2008                     }
   2009                     pw.decreaseIndent();
   2010 
   2011                     pw.println("packageSet:");
   2012                     pw.increaseIndent();
   2013                     for (String packageName : userState.packageSet) {
   2014                         pw.println(packageName);
   2015                     }
   2016                     pw.decreaseIndent();
   2017 
   2018                     pw.println("clientStateMap: ITvInputClient -> ClientState");
   2019                     pw.increaseIndent();
   2020                     for (Map.Entry<IBinder, ClientState> entry :
   2021                             userState.clientStateMap.entrySet()) {
   2022                         ClientState client = entry.getValue();
   2023                         pw.println(entry.getKey() + ": " + client);
   2024 
   2025                         pw.increaseIndent();
   2026 
   2027                         pw.println("sessionTokens:");
   2028                         pw.increaseIndent();
   2029                         for (IBinder token : client.sessionTokens) {
   2030                             pw.println("" + token);
   2031                         }
   2032                         pw.decreaseIndent();
   2033 
   2034                         pw.println("clientTokens: " + client.clientToken);
   2035                         pw.println("userId: " + client.userId);
   2036 
   2037                         pw.decreaseIndent();
   2038                     }
   2039                     pw.decreaseIndent();
   2040 
   2041                     pw.println("serviceStateMap: ComponentName -> ServiceState");
   2042                     pw.increaseIndent();
   2043                     for (Map.Entry<ComponentName, ServiceState> entry :
   2044                             userState.serviceStateMap.entrySet()) {
   2045                         ServiceState service = entry.getValue();
   2046                         pw.println(entry.getKey() + ": " + service);
   2047 
   2048                         pw.increaseIndent();
   2049 
   2050                         pw.println("sessionTokens:");
   2051                         pw.increaseIndent();
   2052                         for (IBinder token : service.sessionTokens) {
   2053                             pw.println("" + token);
   2054                         }
   2055                         pw.decreaseIndent();
   2056 
   2057                         pw.println("service: " + service.service);
   2058                         pw.println("callback: " + service.callback);
   2059                         pw.println("bound: " + service.bound);
   2060                         pw.println("reconnecting: " + service.reconnecting);
   2061 
   2062                         pw.decreaseIndent();
   2063                     }
   2064                     pw.decreaseIndent();
   2065 
   2066                     pw.println("sessionStateMap: ITvInputSession -> SessionState");
   2067                     pw.increaseIndent();
   2068                     for (Map.Entry<IBinder, SessionState> entry :
   2069                             userState.sessionStateMap.entrySet()) {
   2070                         SessionState session = entry.getValue();
   2071                         pw.println(entry.getKey() + ": " + session);
   2072 
   2073                         pw.increaseIndent();
   2074                         pw.println("inputId: " + session.inputId);
   2075                         pw.println("client: " + session.client);
   2076                         pw.println("seq: " + session.seq);
   2077                         pw.println("callingUid: " + session.callingUid);
   2078                         pw.println("userId: " + session.userId);
   2079                         pw.println("sessionToken: " + session.sessionToken);
   2080                         pw.println("session: " + session.session);
   2081                         pw.println("logUri: " + session.logUri);
   2082                         pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
   2083                         pw.decreaseIndent();
   2084                     }
   2085                     pw.decreaseIndent();
   2086 
   2087                     pw.println("callbackSet:");
   2088                     pw.increaseIndent();
   2089                     for (ITvInputManagerCallback callback : userState.callbackSet) {
   2090                         pw.println(callback.toString());
   2091                     }
   2092                     pw.decreaseIndent();
   2093 
   2094                     pw.println("mainSessionToken: " + userState.mainSessionToken);
   2095                     pw.decreaseIndent();
   2096                 }
   2097             }
   2098             mTvInputHardwareManager.dump(fd, writer, args);
   2099         }
   2100     }
   2101 
   2102     private static final class UserState {
   2103         // A mapping from the TV input id to its TvInputState.
   2104         private Map<String, TvInputState> inputMap = new HashMap<>();
   2105 
   2106         // A set of all TV input packages.
   2107         private final Set<String> packageSet = new HashSet<>();
   2108 
   2109         // A list of all TV content rating systems defined.
   2110         private final List<TvContentRatingSystemInfo>
   2111                 contentRatingSystemList = new ArrayList<>();
   2112 
   2113         // A mapping from the token of a client to its state.
   2114         private final Map<IBinder, ClientState> clientStateMap = new HashMap<>();
   2115 
   2116         // A mapping from the name of a TV input service to its state.
   2117         private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>();
   2118 
   2119         // A mapping from the token of a TV input session to its state.
   2120         private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>();
   2121 
   2122         // A set of callbacks.
   2123         private final Set<ITvInputManagerCallback> callbackSet = new HashSet<>();
   2124 
   2125         // The token of a "main" TV input session.
   2126         private IBinder mainSessionToken = null;
   2127 
   2128         // Persistent data store for all internal settings maintained by the TV input manager
   2129         // service.
   2130         private final PersistentDataStore persistentDataStore;
   2131 
   2132         private UserState(Context context, int userId) {
   2133             persistentDataStore = new PersistentDataStore(context, userId);
   2134         }
   2135     }
   2136 
   2137     private final class ClientState implements IBinder.DeathRecipient {
   2138         private final List<IBinder> sessionTokens = new ArrayList<>();
   2139 
   2140         private IBinder clientToken;
   2141         private final int userId;
   2142 
   2143         ClientState(IBinder clientToken, int userId) {
   2144             this.clientToken = clientToken;
   2145             this.userId = userId;
   2146         }
   2147 
   2148         public boolean isEmpty() {
   2149             return sessionTokens.isEmpty();
   2150         }
   2151 
   2152         @Override
   2153         public void binderDied() {
   2154             synchronized (mLock) {
   2155                 UserState userState = getOrCreateUserStateLocked(userId);
   2156                 // DO NOT remove the client state of clientStateMap in this method. It will be
   2157                 // removed in releaseSessionLocked().
   2158                 ClientState clientState = userState.clientStateMap.get(clientToken);
   2159                 if (clientState != null) {
   2160                     while (clientState.sessionTokens.size() > 0) {
   2161                         releaseSessionLocked(
   2162                                 clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
   2163                     }
   2164                 }
   2165                 clientToken = null;
   2166             }
   2167         }
   2168     }
   2169 
   2170     private final class ServiceState {
   2171         private final List<IBinder> sessionTokens = new ArrayList<>();
   2172         private final ServiceConnection connection;
   2173         private final ComponentName component;
   2174         private final boolean isHardware;
   2175         private final Map<String, TvInputInfo> hardwareInputMap = new HashMap<>();
   2176 
   2177         private ITvInputService service;
   2178         private ServiceCallback callback;
   2179         private boolean bound;
   2180         private boolean reconnecting;
   2181 
   2182         private ServiceState(ComponentName component, int userId) {
   2183             this.component = component;
   2184             this.connection = new InputServiceConnection(component, userId);
   2185             this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
   2186         }
   2187     }
   2188 
   2189     private static final class TvInputState {
   2190         // A TvInputInfo object which represents the TV input.
   2191         private TvInputInfo info;
   2192 
   2193         // The state of TV input. Connected by default.
   2194         private int state = INPUT_STATE_CONNECTED;
   2195 
   2196         @Override
   2197         public String toString() {
   2198             return "info: " + info + "; state: " + state;
   2199         }
   2200     }
   2201 
   2202     private final class SessionState implements IBinder.DeathRecipient {
   2203         private final String inputId;
   2204         private final ComponentName componentName;
   2205         private final boolean isRecordingSession;
   2206         private final ITvInputClient client;
   2207         private final int seq;
   2208         private final int callingUid;
   2209         private final int userId;
   2210         private final IBinder sessionToken;
   2211         private ITvInputSession session;
   2212         private Uri logUri;
   2213         // Not null if this session represents an external device connected to a hardware TV input.
   2214         private IBinder hardwareSessionToken;
   2215 
   2216         private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
   2217                 boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
   2218                 int userId) {
   2219             this.sessionToken = sessionToken;
   2220             this.inputId = inputId;
   2221             this.componentName = componentName;
   2222             this.isRecordingSession = isRecordingSession;
   2223             this.client = client;
   2224             this.seq = seq;
   2225             this.callingUid = callingUid;
   2226             this.userId = userId;
   2227         }
   2228 
   2229         @Override
   2230         public void binderDied() {
   2231             synchronized (mLock) {
   2232                 session = null;
   2233                 clearSessionAndNotifyClientLocked(this);
   2234             }
   2235         }
   2236     }
   2237 
   2238     private final class InputServiceConnection implements ServiceConnection {
   2239         private final ComponentName mComponent;
   2240         private final int mUserId;
   2241 
   2242         private InputServiceConnection(ComponentName component, int userId) {
   2243             mComponent = component;
   2244             mUserId = userId;
   2245         }
   2246 
   2247         @Override
   2248         public void onServiceConnected(ComponentName component, IBinder service) {
   2249             if (DEBUG) {
   2250                 Slog.d(TAG, "onServiceConnected(component=" + component + ")");
   2251             }
   2252             synchronized (mLock) {
   2253                 UserState userState = mUserStates.get(mUserId);
   2254                 if (userState == null) {
   2255                     // The user was removed while connecting.
   2256                     mContext.unbindService(this);
   2257                     return;
   2258                 }
   2259                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
   2260                 serviceState.service = ITvInputService.Stub.asInterface(service);
   2261 
   2262                 // Register a callback, if we need to.
   2263                 if (serviceState.isHardware && serviceState.callback == null) {
   2264                     serviceState.callback = new ServiceCallback(mComponent, mUserId);
   2265                     try {
   2266                         serviceState.service.registerCallback(serviceState.callback);
   2267                     } catch (RemoteException e) {
   2268                         Slog.e(TAG, "error in registerCallback", e);
   2269                     }
   2270                 }
   2271 
   2272                 // And create sessions, if any.
   2273                 for (IBinder sessionToken : serviceState.sessionTokens) {
   2274                     createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
   2275                 }
   2276 
   2277                 for (TvInputState inputState : userState.inputMap.values()) {
   2278                     if (inputState.info.getComponent().equals(component)
   2279                             && inputState.state != INPUT_STATE_CONNECTED) {
   2280                         notifyInputStateChangedLocked(userState, inputState.info.getId(),
   2281                                 inputState.state, null);
   2282                     }
   2283                 }
   2284 
   2285                 if (serviceState.isHardware) {
   2286                     serviceState.hardwareInputMap.clear();
   2287                     for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) {
   2288                         try {
   2289                             serviceState.service.notifyHardwareAdded(hardware);
   2290                         } catch (RemoteException e) {
   2291                             Slog.e(TAG, "error in notifyHardwareAdded", e);
   2292                         }
   2293                     }
   2294                     for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
   2295                         try {
   2296                             serviceState.service.notifyHdmiDeviceAdded(device);
   2297                         } catch (RemoteException e) {
   2298                             Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
   2299                         }
   2300                     }
   2301                 }
   2302             }
   2303         }
   2304 
   2305         @Override
   2306         public void onServiceDisconnected(ComponentName component) {
   2307             if (DEBUG) {
   2308                 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")");
   2309             }
   2310             if (!mComponent.equals(component)) {
   2311                 throw new IllegalArgumentException("Mismatched ComponentName: "
   2312                         + mComponent + " (expected), " + component + " (actual).");
   2313             }
   2314             synchronized (mLock) {
   2315                 UserState userState = getOrCreateUserStateLocked(mUserId);
   2316                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
   2317                 if (serviceState != null) {
   2318                     serviceState.reconnecting = true;
   2319                     serviceState.bound = false;
   2320                     serviceState.service = null;
   2321                     serviceState.callback = null;
   2322 
   2323                     abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
   2324                 }
   2325             }
   2326         }
   2327     }
   2328 
   2329     private final class ServiceCallback extends ITvInputServiceCallback.Stub {
   2330         private final ComponentName mComponent;
   2331         private final int mUserId;
   2332 
   2333         ServiceCallback(ComponentName component, int userId) {
   2334             mComponent = component;
   2335             mUserId = userId;
   2336         }
   2337 
   2338         private void ensureHardwarePermission() {
   2339             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
   2340                     != PackageManager.PERMISSION_GRANTED) {
   2341                 throw new SecurityException("The caller does not have hardware permission");
   2342             }
   2343         }
   2344 
   2345         private void ensureValidInput(TvInputInfo inputInfo) {
   2346             if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) {
   2347                 throw new IllegalArgumentException("Invalid TvInputInfo");
   2348             }
   2349         }
   2350 
   2351         private void addHardwareInputLocked(TvInputInfo inputInfo) {
   2352             ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
   2353             serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
   2354             buildTvInputListLocked(mUserId, null);
   2355         }
   2356 
   2357         public void addHardwareInput(int deviceId, TvInputInfo inputInfo) {
   2358             ensureHardwarePermission();
   2359             ensureValidInput(inputInfo);
   2360             synchronized (mLock) {
   2361                 mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
   2362                 addHardwareInputLocked(inputInfo);
   2363             }
   2364         }
   2365 
   2366         public void addHdmiInput(int id, TvInputInfo inputInfo) {
   2367             ensureHardwarePermission();
   2368             ensureValidInput(inputInfo);
   2369             synchronized (mLock) {
   2370                 mTvInputHardwareManager.addHdmiInput(id, inputInfo);
   2371                 addHardwareInputLocked(inputInfo);
   2372             }
   2373         }
   2374 
   2375         public void removeHardwareInput(String inputId) {
   2376             ensureHardwarePermission();
   2377             synchronized (mLock) {
   2378                 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
   2379                 boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
   2380                 if (removed) {
   2381                     buildTvInputListLocked(mUserId, null);
   2382                     mTvInputHardwareManager.removeHardwareInput(inputId);
   2383                 } else {
   2384                     Slog.e(TAG, "failed to remove input " + inputId);
   2385                 }
   2386             }
   2387         }
   2388     }
   2389 
   2390     private final class SessionCallback extends ITvInputSessionCallback.Stub {
   2391         private final SessionState mSessionState;
   2392         private final InputChannel[] mChannels;
   2393 
   2394         SessionCallback(SessionState sessionState, InputChannel[] channels) {
   2395             mSessionState = sessionState;
   2396             mChannels = channels;
   2397         }
   2398 
   2399         @Override
   2400         public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) {
   2401             if (DEBUG) {
   2402                 Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")");
   2403             }
   2404             synchronized (mLock) {
   2405                 mSessionState.session = session;
   2406                 mSessionState.hardwareSessionToken = hardwareSessionToken;
   2407                 if (session != null && addSessionTokenToClientStateLocked(session)) {
   2408                     sendSessionTokenToClientLocked(mSessionState.client,
   2409                             mSessionState.inputId, mSessionState.sessionToken, mChannels[0],
   2410                             mSessionState.seq);
   2411                 } else {
   2412                     removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId);
   2413                     sendSessionTokenToClientLocked(mSessionState.client,
   2414                             mSessionState.inputId, null, null, mSessionState.seq);
   2415                 }
   2416                 mChannels[0].dispose();
   2417             }
   2418         }
   2419 
   2420         private boolean addSessionTokenToClientStateLocked(ITvInputSession session) {
   2421             try {
   2422                 session.asBinder().linkToDeath(mSessionState, 0);
   2423             } catch (RemoteException e) {
   2424                 Slog.e(TAG, "session process has already died", e);
   2425                 return false;
   2426             }
   2427 
   2428             IBinder clientToken = mSessionState.client.asBinder();
   2429             UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
   2430             ClientState clientState = userState.clientStateMap.get(clientToken);
   2431             if (clientState == null) {
   2432                 clientState = new ClientState(clientToken, mSessionState.userId);
   2433                 try {
   2434                     clientToken.linkToDeath(clientState, 0);
   2435                 } catch (RemoteException e) {
   2436                     Slog.e(TAG, "client process has already died", e);
   2437                     return false;
   2438                 }
   2439                 userState.clientStateMap.put(clientToken, clientState);
   2440             }
   2441             clientState.sessionTokens.add(mSessionState.sessionToken);
   2442             return true;
   2443         }
   2444 
   2445         @Override
   2446         public void onChannelRetuned(Uri channelUri) {
   2447             synchronized (mLock) {
   2448                 if (DEBUG) {
   2449                     Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
   2450                 }
   2451                 if (mSessionState.session == null || mSessionState.client == null) {
   2452                     return;
   2453                 }
   2454                 try {
   2455                     // TODO: Consider adding this channel change in the watch log. When we do
   2456                     // that, how we can protect the watch log from malicious tv inputs should
   2457                     // be addressed. e.g. add a field which represents where the channel change
   2458                     // originated from.
   2459                     mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq);
   2460                 } catch (RemoteException e) {
   2461                     Slog.e(TAG, "error in onChannelRetuned", e);
   2462                 }
   2463             }
   2464         }
   2465 
   2466         @Override
   2467         public void onTracksChanged(List<TvTrackInfo> tracks) {
   2468             synchronized (mLock) {
   2469                 if (DEBUG) {
   2470                     Slog.d(TAG, "onTracksChanged(" + tracks + ")");
   2471                 }
   2472                 if (mSessionState.session == null || mSessionState.client == null) {
   2473                     return;
   2474                 }
   2475                 try {
   2476                     mSessionState.client.onTracksChanged(tracks, mSessionState.seq);
   2477                 } catch (RemoteException e) {
   2478                     Slog.e(TAG, "error in onTracksChanged", e);
   2479                 }
   2480             }
   2481         }
   2482 
   2483         @Override
   2484         public void onTrackSelected(int type, String trackId) {
   2485             synchronized (mLock) {
   2486                 if (DEBUG) {
   2487                     Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
   2488                 }
   2489                 if (mSessionState.session == null || mSessionState.client == null) {
   2490                     return;
   2491                 }
   2492                 try {
   2493                     mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq);
   2494                 } catch (RemoteException e) {
   2495                     Slog.e(TAG, "error in onTrackSelected", e);
   2496                 }
   2497             }
   2498         }
   2499 
   2500         @Override
   2501         public void onVideoAvailable() {
   2502             synchronized (mLock) {
   2503                 if (DEBUG) {
   2504                     Slog.d(TAG, "onVideoAvailable()");
   2505                 }
   2506                 if (mSessionState.session == null || mSessionState.client == null) {
   2507                     return;
   2508                 }
   2509                 try {
   2510                     mSessionState.client.onVideoAvailable(mSessionState.seq);
   2511                 } catch (RemoteException e) {
   2512                     Slog.e(TAG, "error in onVideoAvailable", e);
   2513                 }
   2514             }
   2515         }
   2516 
   2517         @Override
   2518         public void onVideoUnavailable(int reason) {
   2519             synchronized (mLock) {
   2520                 if (DEBUG) {
   2521                     Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
   2522                 }
   2523                 if (mSessionState.session == null || mSessionState.client == null) {
   2524                     return;
   2525                 }
   2526                 try {
   2527                     mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
   2528                 } catch (RemoteException e) {
   2529                     Slog.e(TAG, "error in onVideoUnavailable", e);
   2530                 }
   2531             }
   2532         }
   2533 
   2534         @Override
   2535         public void onContentAllowed() {
   2536             synchronized (mLock) {
   2537                 if (DEBUG) {
   2538                     Slog.d(TAG, "onContentAllowed()");
   2539                 }
   2540                 if (mSessionState.session == null || mSessionState.client == null) {
   2541                     return;
   2542                 }
   2543                 try {
   2544                     mSessionState.client.onContentAllowed(mSessionState.seq);
   2545                 } catch (RemoteException e) {
   2546                     Slog.e(TAG, "error in onContentAllowed", e);
   2547                 }
   2548             }
   2549         }
   2550 
   2551         @Override
   2552         public void onContentBlocked(String rating) {
   2553             synchronized (mLock) {
   2554                 if (DEBUG) {
   2555                     Slog.d(TAG, "onContentBlocked()");
   2556                 }
   2557                 if (mSessionState.session == null || mSessionState.client == null) {
   2558                     return;
   2559                 }
   2560                 try {
   2561                     mSessionState.client.onContentBlocked(rating, mSessionState.seq);
   2562                 } catch (RemoteException e) {
   2563                     Slog.e(TAG, "error in onContentBlocked", e);
   2564                 }
   2565             }
   2566         }
   2567 
   2568         @Override
   2569         public void onLayoutSurface(int left, int top, int right, int bottom) {
   2570             synchronized (mLock) {
   2571                 if (DEBUG) {
   2572                     Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
   2573                             + ", right=" + right + ", bottom=" + bottom + ",)");
   2574                 }
   2575                 if (mSessionState.session == null || mSessionState.client == null) {
   2576                     return;
   2577                 }
   2578                 try {
   2579                     mSessionState.client.onLayoutSurface(left, top, right, bottom,
   2580                             mSessionState.seq);
   2581                 } catch (RemoteException e) {
   2582                     Slog.e(TAG, "error in onLayoutSurface", e);
   2583                 }
   2584             }
   2585         }
   2586 
   2587         @Override
   2588         public void onSessionEvent(String eventType, Bundle eventArgs) {
   2589             synchronized (mLock) {
   2590                 if (DEBUG) {
   2591                     Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs
   2592                             + ")");
   2593                 }
   2594                 if (mSessionState.session == null || mSessionState.client == null) {
   2595                     return;
   2596                 }
   2597                 try {
   2598                     mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq);
   2599                 } catch (RemoteException e) {
   2600                     Slog.e(TAG, "error in onSessionEvent", e);
   2601                 }
   2602             }
   2603         }
   2604 
   2605         @Override
   2606         public void onTimeShiftStatusChanged(int status) {
   2607             synchronized (mLock) {
   2608                 if (DEBUG) {
   2609                     Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")");
   2610                 }
   2611                 if (mSessionState.session == null || mSessionState.client == null) {
   2612                     return;
   2613                 }
   2614                 try {
   2615                     mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq);
   2616                 } catch (RemoteException e) {
   2617                     Slog.e(TAG, "error in onTimeShiftStatusChanged", e);
   2618                 }
   2619             }
   2620         }
   2621 
   2622         @Override
   2623         public void onTimeShiftStartPositionChanged(long timeMs) {
   2624             synchronized (mLock) {
   2625                 if (DEBUG) {
   2626                     Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")");
   2627                 }
   2628                 if (mSessionState.session == null || mSessionState.client == null) {
   2629                     return;
   2630                 }
   2631                 try {
   2632                     mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq);
   2633                 } catch (RemoteException e) {
   2634                     Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e);
   2635                 }
   2636             }
   2637         }
   2638 
   2639         @Override
   2640         public void onTimeShiftCurrentPositionChanged(long timeMs) {
   2641             synchronized (mLock) {
   2642                 if (DEBUG) {
   2643                     Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")");
   2644                 }
   2645                 if (mSessionState.session == null || mSessionState.client == null) {
   2646                     return;
   2647                 }
   2648                 try {
   2649                     mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs,
   2650                             mSessionState.seq);
   2651                 } catch (RemoteException e) {
   2652                     Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e);
   2653                 }
   2654             }
   2655         }
   2656 
   2657         // For the recording session only
   2658         @Override
   2659         public void onTuned(Uri channelUri) {
   2660             synchronized (mLock) {
   2661                 if (DEBUG) {
   2662                     Slog.d(TAG, "onTuned()");
   2663                 }
   2664                 if (mSessionState.session == null || mSessionState.client == null) {
   2665                     return;
   2666                 }
   2667                 try {
   2668                     mSessionState.client.onTuned(mSessionState.seq, channelUri);
   2669                 } catch (RemoteException e) {
   2670                     Slog.e(TAG, "error in onTuned", e);
   2671                 }
   2672             }
   2673         }
   2674 
   2675         // For the recording session only
   2676         @Override
   2677         public void onRecordingStopped(Uri recordedProgramUri) {
   2678             synchronized (mLock) {
   2679                 if (DEBUG) {
   2680                     Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri
   2681                             + ")");
   2682                 }
   2683                 if (mSessionState.session == null || mSessionState.client == null) {
   2684                     return;
   2685                 }
   2686                 try {
   2687                     mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq);
   2688                 } catch (RemoteException e) {
   2689                     Slog.e(TAG, "error in onRecordingStopped", e);
   2690                 }
   2691             }
   2692         }
   2693 
   2694         // For the recording session only
   2695         @Override
   2696         public void onError(int error) {
   2697             synchronized (mLock) {
   2698                 if (DEBUG) {
   2699                     Slog.d(TAG, "onError(error=" + error + ")");
   2700                 }
   2701                 if (mSessionState.session == null || mSessionState.client == null) {
   2702                     return;
   2703                 }
   2704                 try {
   2705                     mSessionState.client.onError(error, mSessionState.seq);
   2706                 } catch (RemoteException e) {
   2707                     Slog.e(TAG, "error in onError", e);
   2708                 }
   2709             }
   2710         }
   2711     }
   2712 
   2713     private static final class WatchLogHandler extends Handler {
   2714         // There are only two kinds of watch events that can happen on the system:
   2715         // 1. The current TV input session is tuned to a new channel.
   2716         // 2. The session is released for some reason.
   2717         // The former indicates the end of the previous log entry, if any, followed by the start of
   2718         // a new entry. The latter indicates the end of the most recent entry for the given session.
   2719         // Here the system supplies the database the smallest set of information only that is
   2720         // sufficient to consolidate the log entries while minimizing database operations in the
   2721         // system service.
   2722         static final int MSG_LOG_WATCH_START = 1;
   2723         static final int MSG_LOG_WATCH_END = 2;
   2724         static final int MSG_SWITCH_CONTENT_RESOLVER = 3;
   2725 
   2726         private ContentResolver mContentResolver;
   2727 
   2728         WatchLogHandler(ContentResolver contentResolver, Looper looper) {
   2729             super(looper);
   2730             mContentResolver = contentResolver;
   2731         }
   2732 
   2733         @Override
   2734         public void handleMessage(Message msg) {
   2735             switch (msg.what) {
   2736                 case MSG_LOG_WATCH_START: {
   2737                     SomeArgs args = (SomeArgs) msg.obj;
   2738                     String packageName = (String) args.arg1;
   2739                     long watchStartTime = (long) args.arg2;
   2740                     long channelId = (long) args.arg3;
   2741                     Bundle tuneParams = (Bundle) args.arg4;
   2742                     IBinder sessionToken = (IBinder) args.arg5;
   2743 
   2744                     ContentValues values = new ContentValues();
   2745                     values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
   2746                     values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
   2747                             watchStartTime);
   2748                     values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
   2749                     if (tuneParams != null) {
   2750                         values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS,
   2751                                 encodeTuneParams(tuneParams));
   2752                     }
   2753                     values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
   2754                             sessionToken.toString());
   2755 
   2756                     mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
   2757                     args.recycle();
   2758                     break;
   2759                 }
   2760                 case MSG_LOG_WATCH_END: {
   2761                     SomeArgs args = (SomeArgs) msg.obj;
   2762                     IBinder sessionToken = (IBinder) args.arg1;
   2763                     long watchEndTime = (long) args.arg2;
   2764 
   2765                     ContentValues values = new ContentValues();
   2766                     values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
   2767                             watchEndTime);
   2768                     values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
   2769                             sessionToken.toString());
   2770 
   2771                     mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
   2772                     args.recycle();
   2773                     break;
   2774                 }
   2775                 case MSG_SWITCH_CONTENT_RESOLVER: {
   2776                     mContentResolver = (ContentResolver) msg.obj;
   2777                     break;
   2778                 }
   2779                 default: {
   2780                     Slog.w(TAG, "unhandled message code: " + msg.what);
   2781                     break;
   2782                 }
   2783             }
   2784         }
   2785 
   2786         private String encodeTuneParams(Bundle tuneParams) {
   2787             StringBuilder builder = new StringBuilder();
   2788             Set<String> keySet = tuneParams.keySet();
   2789             Iterator<String> it = keySet.iterator();
   2790             while (it.hasNext()) {
   2791                 String key = it.next();
   2792                 Object value = tuneParams.get(key);
   2793                 if (value == null) {
   2794                     continue;
   2795                 }
   2796                 builder.append(replaceEscapeCharacters(key));
   2797                 builder.append("=");
   2798                 builder.append(replaceEscapeCharacters(value.toString()));
   2799                 if (it.hasNext()) {
   2800                     builder.append(", ");
   2801                 }
   2802             }
   2803             return builder.toString();
   2804         }
   2805 
   2806         private String replaceEscapeCharacters(String src) {
   2807             final char ESCAPE_CHARACTER = '%';
   2808             final String ENCODING_TARGET_CHARACTERS = "%=,";
   2809             StringBuilder builder = new StringBuilder();
   2810             for (char ch : src.toCharArray()) {
   2811                 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) {
   2812                     builder.append(ESCAPE_CHARACTER);
   2813                 }
   2814                 builder.append(ch);
   2815             }
   2816             return builder.toString();
   2817         }
   2818     }
   2819 
   2820     private final class HardwareListener implements TvInputHardwareManager.Listener {
   2821         @Override
   2822         public void onStateChanged(String inputId, int state) {
   2823             synchronized (mLock) {
   2824                 setStateLocked(inputId, state, mCurrentUserId);
   2825             }
   2826         }
   2827 
   2828         @Override
   2829         public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
   2830             synchronized (mLock) {
   2831                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
   2832                 // Broadcast the event to all hardware inputs.
   2833                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
   2834                     if (!serviceState.isHardware || serviceState.service == null) continue;
   2835                     try {
   2836                         serviceState.service.notifyHardwareAdded(info);
   2837                     } catch (RemoteException e) {
   2838                         Slog.e(TAG, "error in notifyHardwareAdded", e);
   2839                     }
   2840                 }
   2841             }
   2842         }
   2843 
   2844         @Override
   2845         public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
   2846             synchronized (mLock) {
   2847                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
   2848                 // Broadcast the event to all hardware inputs.
   2849                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
   2850                     if (!serviceState.isHardware || serviceState.service == null) continue;
   2851                     try {
   2852                         serviceState.service.notifyHardwareRemoved(info);
   2853                     } catch (RemoteException e) {
   2854                         Slog.e(TAG, "error in notifyHardwareRemoved", e);
   2855                     }
   2856                 }
   2857             }
   2858         }
   2859 
   2860         @Override
   2861         public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
   2862             synchronized (mLock) {
   2863                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
   2864                 // Broadcast the event to all hardware inputs.
   2865                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
   2866                     if (!serviceState.isHardware || serviceState.service == null) continue;
   2867                     try {
   2868                         serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
   2869                     } catch (RemoteException e) {
   2870                         Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
   2871                     }
   2872                 }
   2873             }
   2874         }
   2875 
   2876         @Override
   2877         public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
   2878             synchronized (mLock) {
   2879                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
   2880                 // Broadcast the event to all hardware inputs.
   2881                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
   2882                     if (!serviceState.isHardware || serviceState.service == null) continue;
   2883                     try {
   2884                         serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
   2885                     } catch (RemoteException e) {
   2886                         Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
   2887                     }
   2888                 }
   2889             }
   2890         }
   2891 
   2892         @Override
   2893         public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
   2894             synchronized (mLock) {
   2895                 Integer state;
   2896                 switch (deviceInfo.getDevicePowerStatus()) {
   2897                     case HdmiControlManager.POWER_STATUS_ON:
   2898                         state = INPUT_STATE_CONNECTED;
   2899                         break;
   2900                     case HdmiControlManager.POWER_STATUS_STANDBY:
   2901                     case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
   2902                     case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
   2903                         state = INPUT_STATE_CONNECTED_STANDBY;
   2904                         break;
   2905                     case HdmiControlManager.POWER_STATUS_UNKNOWN:
   2906                     default:
   2907                         state = null;
   2908                         break;
   2909                 }
   2910                 if (state != null) {
   2911                     setStateLocked(inputId, state, mCurrentUserId);
   2912                 }
   2913             }
   2914         }
   2915     }
   2916 
   2917     private static class SessionNotFoundException extends IllegalArgumentException {
   2918         public SessionNotFoundException(String name) {
   2919             super(name);
   2920         }
   2921     }
   2922 }
   2923