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.AudioManager.DEVICE_NONE;
20 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
21 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.UserIdInt;
26 import android.app.ActivityManager;
27 import android.content.BroadcastReceiver;
28 import android.content.ComponentName;
29 import android.content.ContentResolver;
30 import android.content.ContentUris;
31 import android.content.ContentValues;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.ServiceConnection;
36 import android.content.pm.ActivityInfo;
37 import android.content.pm.ApplicationInfo;
38 import android.content.pm.PackageManager;
39 import android.content.pm.PackageManager.NameNotFoundException;
40 import android.content.pm.ResolveInfo;
41 import android.content.pm.ServiceInfo;
42 import android.content.pm.UserInfo;
43 import android.graphics.Rect;
44 import android.hardware.hdmi.HdmiControlManager;
45 import android.hardware.hdmi.HdmiDeviceInfo;
46 import android.media.PlaybackParams;
47 import android.media.tv.DvbDeviceInfo;
48 import android.media.tv.ITvInputClient;
49 import android.media.tv.ITvInputHardware;
50 import android.media.tv.ITvInputHardwareCallback;
51 import android.media.tv.ITvInputManager;
52 import android.media.tv.ITvInputManagerCallback;
53 import android.media.tv.ITvInputService;
54 import android.media.tv.ITvInputServiceCallback;
55 import android.media.tv.ITvInputSession;
56 import android.media.tv.ITvInputSessionCallback;
57 import android.media.tv.TunedInfo;
58 import android.media.tv.TvContentRating;
59 import android.media.tv.TvContentRatingSystemInfo;
60 import android.media.tv.TvContract;
61 import android.media.tv.TvInputHardwareInfo;
62 import android.media.tv.TvInputInfo;
63 import android.media.tv.TvInputManager;
64 import android.media.tv.TvInputService;
65 import android.media.tv.TvStreamConfig;
66 import android.media.tv.TvTrackInfo;
67 import android.net.Uri;
68 import android.os.Binder;
69 import android.os.Bundle;
70 import android.os.Handler;
71 import android.os.IBinder;
72 import android.os.Looper;
73 import android.os.Message;
74 import android.os.ParcelFileDescriptor;
75 import android.os.Process;
76 import android.os.RemoteCallbackList;
77 import android.os.RemoteException;
78 import android.os.UserHandle;
79 import android.os.UserManager;
80 import android.text.TextUtils;
81 import android.util.ArrayMap;
82 import android.util.Pair;
83 import android.util.Slog;
84 import android.util.SparseArray;
85 import android.view.InputChannel;
86 import android.view.Surface;
87 
88 import com.android.internal.annotations.GuardedBy;
89 import com.android.internal.annotations.VisibleForTesting;
90 import com.android.internal.content.PackageMonitor;
91 import com.android.internal.os.SomeArgs;
92 import com.android.internal.util.DumpUtils;
93 import com.android.internal.util.FrameworkStatsLog;
94 import com.android.internal.util.IndentingPrintWriter;
95 import com.android.server.IoThread;
96 import com.android.server.SystemService;
97 
98 import java.io.File;
99 import java.io.FileDescriptor;
100 import java.io.FileNotFoundException;
101 import java.io.PrintWriter;
102 import java.util.ArrayList;
103 import java.util.Arrays;
104 import java.util.Collections;
105 import java.util.Comparator;
106 import java.util.HashMap;
107 import java.util.HashSet;
108 import java.util.Iterator;
109 import java.util.List;
110 import java.util.Map;
111 import java.util.Objects;
112 import java.util.Set;
113 import java.util.UUID;
114 import java.util.regex.Matcher;
115 import java.util.regex.Pattern;
116 
117 /** This class provides a system service that manages television inputs. */
118 public final class TvInputManagerService extends SystemService {
119     private static final boolean DEBUG = false;
120     private static final String TAG = "TvInputManagerService";
121     private static final String DVB_DIRECTORY = "/dev/dvb";
122     private static final int APP_TAG_SELF = TunedInfo.APP_TAG_SELF;
123     private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
124             "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";
125 
126     // There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
127     // another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
128     // DVB frontend devices from the list of files in the /dev and /dev/dvb/adapter%d directory.
129     private static final Pattern sFrontEndDevicePattern =
130             Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$");
131     private static final Pattern sAdapterDirPattern =
132             Pattern.compile("^adapter([0-9]+)$");
133     private static final Pattern sFrontEndInAdapterDirPattern =
134             Pattern.compile("^frontend([0-9]+)$");
135 
136     private final Context mContext;
137     private final TvInputHardwareManager mTvInputHardwareManager;
138     private final UserManager mUserManager;
139 
140     // A global lock.
141     private final Object mLock = new Object();
142 
143     // ID of the current user.
144     @GuardedBy("mLock")
145     private int mCurrentUserId = UserHandle.USER_SYSTEM;
146     // IDs of the running profiles. Their parent user ID should be mCurrentUserId.
147     @GuardedBy("mLock")
148     private final Set<Integer> mRunningProfiles = new HashSet<>();
149 
150     // A map from user id to UserState.
151     @GuardedBy("mLock")
152     private final SparseArray<UserState> mUserStates = new SparseArray<>();
153 
154     // A map from session id to session state saved in userstate
155     @GuardedBy("mLock")
156     private final Map<String, SessionState> mSessionIdToSessionStateMap = new HashMap<>();
157 
158     private final WatchLogHandler mWatchLogHandler;
159 
160     private final ActivityManager mActivityManager;
161 
TvInputManagerService(Context context)162     public TvInputManagerService(Context context) {
163         super(context);
164 
165         mContext = context;
166         mWatchLogHandler = new WatchLogHandler(mContext.getContentResolver(),
167                 IoThread.get().getLooper());
168         mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
169 
170         mActivityManager =
171                 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
172         mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
173 
174         synchronized (mLock) {
175             getOrCreateUserStateLocked(mCurrentUserId);
176         }
177     }
178 
179     @Override
onStart()180     public void onStart() {
181         publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
182     }
183 
184     @Override
onBootPhase(int phase)185     public void onBootPhase(int phase) {
186         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
187             registerBroadcastReceivers();
188         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
189             synchronized (mLock) {
190                 buildTvInputListLocked(mCurrentUserId, null);
191                 buildTvContentRatingSystemListLocked(mCurrentUserId);
192             }
193         }
194         mTvInputHardwareManager.onBootPhase(phase);
195     }
196 
197     @Override
onUserUnlocking(@onNull TargetUser user)198     public void onUserUnlocking(@NonNull TargetUser user) {
199         if (DEBUG) Slog.d(TAG, "onUnlockUser(user=" + user + ")");
200         synchronized (mLock) {
201             if (mCurrentUserId != user.getUserIdentifier()) {
202                 return;
203             }
204             buildTvInputListLocked(mCurrentUserId, null);
205             buildTvContentRatingSystemListLocked(mCurrentUserId);
206         }
207     }
208 
registerBroadcastReceivers()209     private void registerBroadcastReceivers() {
210         PackageMonitor monitor = new PackageMonitor() {
211             private void buildTvInputList(String[] packages) {
212                 int userId = getChangingUserId();
213                 synchronized (mLock) {
214                     if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
215                         buildTvInputListLocked(userId, packages);
216                         buildTvContentRatingSystemListLocked(userId);
217                     }
218                 }
219             }
220 
221             @Override
222             public void onPackageUpdateFinished(String packageName, int uid) {
223                 if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
224                 // This callback is invoked when the TV input is reinstalled.
225                 // In this case, isReplacing() always returns true.
226                 buildTvInputList(new String[] { packageName });
227             }
228 
229             @Override
230             public void onPackagesAvailable(String[] packages) {
231                 if (DEBUG) {
232                     Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
233                 }
234                 // This callback is invoked when the media on which some packages exist become
235                 // available.
236                 if (isReplacing()) {
237                     buildTvInputList(packages);
238                 }
239             }
240 
241             @Override
242             public void onPackagesUnavailable(String[] packages) {
243                 // This callback is invoked when the media on which some packages exist become
244                 // unavailable.
245                 if (DEBUG)  {
246                     Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
247                             + ")");
248                 }
249                 if (isReplacing()) {
250                     buildTvInputList(packages);
251                 }
252             }
253 
254             @Override
255             public void onSomePackagesChanged() {
256                 // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
257                 // the TV inputs.
258                 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
259                 if (isReplacing()) {
260                     if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
261                     // When the package is updated, buildTvInputListLocked is called in other
262                     // methods instead.
263                     return;
264                 }
265                 buildTvInputList(null);
266             }
267 
268             @Override
269             public boolean onPackageChanged(String packageName, int uid, String[] components) {
270                 // The input list needs to be updated in any cases, regardless of whether
271                 // it happened to the whole package or a specific component. Returning true so that
272                 // the update can be handled in {@link #onSomePackagesChanged}.
273                 return true;
274             }
275         };
276         monitor.register(mContext, null, UserHandle.ALL, true);
277 
278         IntentFilter intentFilter = new IntentFilter();
279         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
280         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
281         intentFilter.addAction(Intent.ACTION_USER_STARTED);
282         intentFilter.addAction(Intent.ACTION_USER_STOPPED);
283         mContext.registerReceiverAsUser(new BroadcastReceiver() {
284             @Override
285             public void onReceive(Context context, Intent intent) {
286                 String action = intent.getAction();
287                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
288                     switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
289                 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
290                     removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
291                 } else if (Intent.ACTION_USER_STARTED.equals(action)) {
292                     int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
293                     startUser(userId);
294                 } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
295                     int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
296                     stopUser(userId);
297                 }
298             }
299         }, UserHandle.ALL, intentFilter, null, null);
300     }
301 
hasHardwarePermission(PackageManager pm, ComponentName component)302     private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) {
303         return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE,
304                 component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
305     }
306     @GuardedBy("mLock")
buildTvInputListLocked(int userId, String[] updatedPackages)307     private void buildTvInputListLocked(int userId, String[] updatedPackages) {
308         UserState userState = getOrCreateUserStateLocked(userId);
309         userState.packageSet.clear();
310 
311         if (DEBUG) Slog.d(TAG, "buildTvInputList");
312         PackageManager pm = mContext.getPackageManager();
313         List<ResolveInfo> services = pm.queryIntentServicesAsUser(
314                 new Intent(TvInputService.SERVICE_INTERFACE),
315                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
316                 userId);
317         List<TvInputInfo> inputList = new ArrayList<>();
318         for (ResolveInfo ri : services) {
319             ServiceInfo si = ri.serviceInfo;
320             if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
321                 Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
322                         + android.Manifest.permission.BIND_TV_INPUT);
323                 continue;
324             }
325 
326             ComponentName component = new ComponentName(si.packageName, si.name);
327             if (hasHardwarePermission(pm, component)) {
328                 ServiceState serviceState = userState.serviceStateMap.get(component);
329                 if (serviceState == null) {
330                     // New hardware input found. Create a new ServiceState and connect to the
331                     // service to populate the hardware list.
332                     serviceState = new ServiceState(component, userId);
333                     userState.serviceStateMap.put(component, serviceState);
334                     updateServiceConnectionLocked(component, userId);
335                 } else {
336                     inputList.addAll(serviceState.hardwareInputMap.values());
337                 }
338             } else {
339                 try {
340                     TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
341                     inputList.add(info);
342                 } catch (Exception e) {
343                     Slog.e(TAG, "failed to load TV input " + si.name, e);
344                     continue;
345                 }
346             }
347             userState.packageSet.add(si.packageName);
348         }
349 
350         // sort the input list by input id so that TvInputState.inputNumber is stable.
351         Collections.sort(inputList, Comparator.comparing(TvInputInfo::getId));
352         Map<String, TvInputState> inputMap = new HashMap<>();
353         ArrayMap<String, Integer> tisInputCount = new ArrayMap<>(inputMap.size());
354         for (TvInputInfo info : inputList) {
355             String inputId = info.getId();
356             if (DEBUG) {
357                 Slog.d(TAG, "add " + inputId);
358             }
359             // Running count of input for each input service
360             Integer count = tisInputCount.get(inputId);
361             count = count == null ? Integer.valueOf(1) : count + 1;
362             tisInputCount.put(inputId, count);
363             TvInputState inputState = userState.inputMap.get(inputId);
364             if (inputState == null) {
365                 inputState = new TvInputState();
366             }
367             inputState.info = info;
368             inputState.uid = getInputUid(info);
369             inputMap.put(inputId, inputState);
370             inputState.inputNumber = count;
371         }
372 
373         for (String inputId : inputMap.keySet()) {
374             if (!userState.inputMap.containsKey(inputId)) {
375                 notifyInputAddedLocked(userState, inputId);
376             } else if (updatedPackages != null) {
377                 // Notify the package updates
378                 ComponentName component = inputMap.get(inputId).info.getComponent();
379                 for (String updatedPackage : updatedPackages) {
380                     if (component.getPackageName().equals(updatedPackage)) {
381                         updateServiceConnectionLocked(component, userId);
382                         notifyInputUpdatedLocked(userState, inputId);
383                         break;
384                     }
385                 }
386             }
387         }
388 
389         for (String inputId : userState.inputMap.keySet()) {
390             if (!inputMap.containsKey(inputId)) {
391                 TvInputInfo info = userState.inputMap.get(inputId).info;
392                 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
393                 if (serviceState != null) {
394                     abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
395                 }
396                 notifyInputRemovedLocked(userState, inputId);
397             }
398         }
399 
400         userState.inputMap.clear();
401         userState.inputMap = inputMap;
402     }
403 
getInputUid(TvInputInfo info)404     private int getInputUid(TvInputInfo info) {
405         try {
406             return getContext().getPackageManager().getApplicationInfo(
407                     info.getServiceInfo().packageName, 0).uid;
408         } catch (NameNotFoundException e) {
409             Slog.w(TAG, "Unable to get UID for  " + info, e);
410             return Process.INVALID_UID;
411         }
412     }
413 
414     @GuardedBy("mLock")
buildTvContentRatingSystemListLocked(int userId)415     private void buildTvContentRatingSystemListLocked(int userId) {
416         UserState userState = getOrCreateUserStateLocked(userId);
417         userState.contentRatingSystemList.clear();
418 
419         final PackageManager pm = mContext.getPackageManager();
420         Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS);
421         for (ResolveInfo resolveInfo :
422                 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) {
423             ActivityInfo receiver = resolveInfo.activityInfo;
424             Bundle metaData = receiver.metaData;
425             if (metaData == null) {
426                 continue;
427             }
428 
429             int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS);
430             if (xmlResId == 0) {
431                 Slog.w(TAG, "Missing meta-data '"
432                         + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver "
433                         + receiver.packageName + "/" + receiver.name);
434                 continue;
435             }
436             userState.contentRatingSystemList.add(
437                     TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId,
438                             receiver.applicationInfo));
439         }
440     }
441 
startUser(int userId)442     private void startUser(int userId) {
443         synchronized (mLock) {
444             if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
445                 // user already started
446                 return;
447             }
448             UserInfo userInfo = mUserManager.getUserInfo(userId);
449             UserInfo parentInfo = mUserManager.getProfileParent(userId);
450             if (userInfo.isProfile()
451                     && parentInfo != null
452                     && parentInfo.id == mCurrentUserId) {
453                 // only the children of the current user can be started in background
454                 startProfileLocked(userId);
455             }
456         }
457     }
458 
stopUser(int userId)459     private void stopUser(int userId) {
460         if (userId == mCurrentUserId) {
461             switchUser(ActivityManager.getCurrentUser());
462             return;
463         }
464 
465         releaseSessionOfUserLocked(userId);
466         unbindServiceOfUserLocked(userId);
467         mRunningProfiles.remove(userId);
468     }
469 
startProfileLocked(int userId)470     private void startProfileLocked(int userId) {
471         mRunningProfiles.add(userId);
472         buildTvInputListLocked(userId, null);
473         buildTvContentRatingSystemListLocked(userId);
474     }
475 
switchUser(int userId)476     private void switchUser(int userId) {
477         synchronized (mLock) {
478             if (mCurrentUserId == userId) {
479                 return;
480             }
481             UserInfo userInfo = mUserManager.getUserInfo(userId);
482             if (userInfo.isProfile()) {
483                 Slog.w(TAG, "cannot switch to a profile!");
484                 return;
485             }
486 
487             for (int runningId : mRunningProfiles) {
488                 releaseSessionOfUserLocked(runningId);
489                 unbindServiceOfUserLocked(runningId);
490             }
491             mRunningProfiles.clear();
492             releaseSessionOfUserLocked(mCurrentUserId);
493             unbindServiceOfUserLocked(mCurrentUserId);
494 
495             mCurrentUserId = userId;
496             buildTvInputListLocked(userId, null);
497             buildTvContentRatingSystemListLocked(userId);
498             mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_SWITCH_CONTENT_RESOLVER,
499                     getContentResolverForUser(userId)).sendToTarget();
500         }
501     }
502 
503     @GuardedBy("mLock")
releaseSessionOfUserLocked(int userId)504     private void releaseSessionOfUserLocked(int userId) {
505         UserState userState = getUserStateLocked(userId);
506         if (userState == null) {
507             return;
508         }
509         List<SessionState> sessionStatesToRelease = new ArrayList<>();
510         for (SessionState sessionState : userState.sessionStateMap.values()) {
511             if (sessionState.session != null && !sessionState.isRecordingSession) {
512                 sessionStatesToRelease.add(sessionState);
513             }
514         }
515         boolean notifyInfoUpdated = false;
516         for (SessionState sessionState : sessionStatesToRelease) {
517             try {
518                 sessionState.session.release();
519                 sessionState.currentChannel = null;
520                 if (sessionState.isCurrent) {
521                     sessionState.isCurrent = false;
522                     notifyInfoUpdated = true;
523                 }
524             } catch (RemoteException e) {
525                 Slog.e(TAG, "error in release", e);
526             } finally {
527                 if (notifyInfoUpdated) {
528                     notifyCurrentChannelInfosUpdatedLocked(userState);
529                 }
530             }
531             clearSessionAndNotifyClientLocked(sessionState);
532         }
533     }
534 
535     @GuardedBy("mLock")
unbindServiceOfUserLocked(int userId)536     private void unbindServiceOfUserLocked(int userId) {
537         UserState userState = getUserStateLocked(userId);
538         if (userState == null) {
539             return;
540         }
541         for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator();
542                 it.hasNext(); ) {
543             ComponentName component = it.next();
544             ServiceState serviceState = userState.serviceStateMap.get(component);
545             if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
546                 if (serviceState.callback != null) {
547                     try {
548                         serviceState.service.unregisterCallback(serviceState.callback);
549                     } catch (RemoteException e) {
550                         Slog.e(TAG, "error in unregisterCallback", e);
551                     }
552                 }
553                 mContext.unbindService(serviceState.connection);
554                 it.remove();
555             }
556         }
557     }
558 
559     @GuardedBy("mLock")
clearSessionAndNotifyClientLocked(SessionState state)560     private void clearSessionAndNotifyClientLocked(SessionState state) {
561         if (state.client != null) {
562             try {
563                 state.client.onSessionReleased(state.seq);
564             } catch(RemoteException e) {
565                 Slog.e(TAG, "error in onSessionReleased", e);
566             }
567         }
568         // If there are any other sessions based on this session, they should be released.
569         UserState userState = getOrCreateUserStateLocked(state.userId);
570         for (SessionState sessionState : userState.sessionStateMap.values()) {
571             if (state.sessionToken == sessionState.hardwareSessionToken) {
572                 releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID, state.userId);
573                 try {
574                     sessionState.client.onSessionReleased(sessionState.seq);
575                 } catch (RemoteException e) {
576                     Slog.e(TAG, "error in onSessionReleased", e);
577                 }
578             }
579         }
580         removeSessionStateLocked(state.sessionToken, state.userId);
581     }
582 
removeUser(int userId)583     private void removeUser(int userId) {
584         synchronized (mLock) {
585             UserState userState = getUserStateLocked(userId);
586             if (userState == null) {
587                 return;
588             }
589             // Release all created sessions.
590             boolean notifyInfoUpdated = false;
591             for (SessionState state : userState.sessionStateMap.values()) {
592                 if (state.session != null) {
593                     try {
594                         state.session.release();
595                         state.currentChannel = null;
596                         if (state.isCurrent) {
597                             state.isCurrent = false;
598                             notifyInfoUpdated = true;
599                         }
600                     } catch (RemoteException e) {
601                         Slog.e(TAG, "error in release", e);
602                     } finally {
603                         if (notifyInfoUpdated) {
604                             notifyCurrentChannelInfosUpdatedLocked(userState);
605                         }
606                     }
607                 }
608             }
609             userState.sessionStateMap.clear();
610 
611             // Unregister all callbacks and unbind all services.
612             for (ServiceState serviceState : userState.serviceStateMap.values()) {
613                 if (serviceState.service != null) {
614                     if (serviceState.callback != null) {
615                         try {
616                             serviceState.service.unregisterCallback(serviceState.callback);
617                         } catch (RemoteException e) {
618                             Slog.e(TAG, "error in unregisterCallback", e);
619                         }
620                     }
621                     mContext.unbindService(serviceState.connection);
622                 }
623             }
624             userState.serviceStateMap.clear();
625 
626             // Clear everything else.
627             userState.inputMap.clear();
628             userState.packageSet.clear();
629             userState.contentRatingSystemList.clear();
630             userState.clientStateMap.clear();
631             userState.mCallbacks.kill();
632             userState.mainSessionToken = null;
633 
634             mRunningProfiles.remove(userId);
635             mUserStates.remove(userId);
636 
637             if (userId == mCurrentUserId) {
638                 switchUser(UserHandle.USER_SYSTEM);
639             }
640         }
641     }
642 
getContentResolverForUser(int userId)643     private ContentResolver getContentResolverForUser(int userId) {
644         UserHandle user = new UserHandle(userId);
645         Context context;
646         try {
647             context = mContext.createPackageContextAsUser("android", 0, user);
648         } catch (NameNotFoundException e) {
649             Slog.e(TAG, "failed to create package context as user " + user);
650             context = mContext;
651         }
652         return context.getContentResolver();
653     }
654 
655     @GuardedBy("mLock")
getOrCreateUserStateLocked(int userId)656     private UserState getOrCreateUserStateLocked(int userId) {
657         UserState userState = getUserStateLocked(userId);
658         if (userState == null) {
659             userState = new UserState(mContext, userId);
660             mUserStates.put(userId, userState);
661         }
662         return userState;
663     }
664 
665     @GuardedBy("mLock")
getServiceStateLocked(ComponentName component, int userId)666     private ServiceState getServiceStateLocked(ComponentName component, int userId) {
667         UserState userState = getOrCreateUserStateLocked(userId);
668         ServiceState serviceState = userState.serviceStateMap.get(component);
669         if (serviceState == null) {
670             throw new IllegalStateException("Service state not found for " + component + " (userId="
671                     + userId + ")");
672         }
673         return serviceState;
674     }
675     @GuardedBy("mLock")
getSessionStateLocked(IBinder sessionToken, int callingUid, int userId)676     private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
677         UserState userState = getOrCreateUserStateLocked(userId);
678         return getSessionStateLocked(sessionToken, callingUid, userState);
679     }
680 
681     @GuardedBy("mLock")
getSessionStateLocked(IBinder sessionToken, int callingUid, UserState userState)682     private SessionState getSessionStateLocked(IBinder sessionToken,
683             int callingUid, UserState userState) {
684         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
685         if (sessionState == null) {
686             throw new SessionNotFoundException("Session state not found for token " + sessionToken);
687         }
688         // Only the application that requested this session or the system can access it.
689         if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
690             throw new SecurityException("Illegal access to the session with token " + sessionToken
691                     + " from uid " + callingUid);
692         }
693         return sessionState;
694     }
695 
696     @GuardedBy("mLock")
getSessionLocked(IBinder sessionToken, int callingUid, int userId)697     private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
698         return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
699     }
700 
701     @GuardedBy("mLock")
getSessionLocked(SessionState sessionState)702     private ITvInputSession getSessionLocked(SessionState sessionState) {
703         ITvInputSession session = sessionState.session;
704         if (session == null) {
705             throw new IllegalStateException("Session not yet created for token "
706                     + sessionState.sessionToken);
707         }
708         return session;
709     }
710 
resolveCallingUserId(int callingPid, int callingUid, int requestedUserId, String methodName)711     private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
712             String methodName) {
713         return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
714                 false, methodName, null);
715     }
716 
717     @GuardedBy("mLock")
updateServiceConnectionLocked(ComponentName component, int userId)718     private void updateServiceConnectionLocked(ComponentName component, int userId) {
719         UserState userState = getOrCreateUserStateLocked(userId);
720         ServiceState serviceState = userState.serviceStateMap.get(component);
721         if (serviceState == null) {
722             return;
723         }
724         if (serviceState.reconnecting) {
725             if (!serviceState.sessionTokens.isEmpty()) {
726                 // wait until all the sessions are removed.
727                 return;
728             }
729             serviceState.reconnecting = false;
730         }
731 
732         boolean shouldBind;
733         if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
734             shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
735         } else {
736             // For a non-current user,
737             // if sessionTokens is not empty, it contains recording sessions only
738             // because other sessions must have been removed while switching user
739             // and non-recording sessions are not created by createSession().
740             shouldBind = !serviceState.sessionTokens.isEmpty();
741         }
742 
743         if (serviceState.service == null && shouldBind) {
744             // This means that the service is not yet connected but its state indicates that we
745             // have pending requests. Then, connect the service.
746             if (serviceState.bound) {
747                 // We have already bound to the service so we don't try to bind again until after we
748                 // unbind later on.
749                 return;
750             }
751             if (DEBUG) {
752                 Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
753             }
754 
755             Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
756             serviceState.bound = mContext.bindServiceAsUser(
757                     i, serviceState.connection,
758                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
759                     new UserHandle(userId));
760         } else if (serviceState.service != null && !shouldBind) {
761             // This means that the service is already connected but its state indicates that we have
762             // nothing to do with it. Then, disconnect the service.
763             if (DEBUG) {
764                 Slog.d(TAG, "unbindService(service=" + component + ")");
765             }
766             mContext.unbindService(serviceState.connection);
767             userState.serviceStateMap.remove(component);
768         }
769     }
770 
771     @GuardedBy("mLock")
abortPendingCreateSessionRequestsLocked(ServiceState serviceState, String inputId, int userId)772     private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
773             String inputId, int userId) {
774         // Let clients know the create session requests are failed.
775         UserState userState = getOrCreateUserStateLocked(userId);
776         List<SessionState> sessionsToAbort = new ArrayList<>();
777         for (IBinder sessionToken : serviceState.sessionTokens) {
778             SessionState sessionState = userState.sessionStateMap.get(sessionToken);
779             if (sessionState.session == null && (inputId == null
780                     || sessionState.inputId.equals(inputId))) {
781                 sessionsToAbort.add(sessionState);
782             }
783         }
784         for (SessionState sessionState : sessionsToAbort) {
785             removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
786             sendSessionTokenToClientLocked(sessionState.client,
787                     sessionState.inputId, null, null, sessionState.seq);
788         }
789         updateServiceConnectionLocked(serviceState.component, userId);
790     }
791 
792     @GuardedBy("mLock")
createSessionInternalLocked(ITvInputService service, IBinder sessionToken, int userId)793     private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
794             int userId) {
795         UserState userState = getOrCreateUserStateLocked(userId);
796         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
797         if (DEBUG) {
798             Slog.d(TAG, "createSessionInternalLocked(inputId="
799                     + sessionState.inputId + ", sessionId=" + sessionState.sessionId + ")");
800         }
801         InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
802 
803         // Set up a callback to send the session token.
804         ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
805 
806         boolean created = true;
807         // Create a session. When failed, send a null token immediately.
808         try {
809             if (sessionState.isRecordingSession) {
810                 service.createRecordingSession(
811                         callback, sessionState.inputId, sessionState.sessionId);
812             } else {
813                 service.createSession(
814                         channels[1], callback, sessionState.inputId, sessionState.sessionId);
815             }
816         } catch (RemoteException e) {
817             Slog.e(TAG, "error in createSession", e);
818             sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
819                     null, sessionState.seq);
820             created = false;
821         }
822         channels[1].dispose();
823         return created;
824     }
825 
826     @GuardedBy("mLock")
sendSessionTokenToClientLocked(ITvInputClient client, String inputId, IBinder sessionToken, InputChannel channel, int seq)827     private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
828             IBinder sessionToken, InputChannel channel, int seq) {
829         try {
830             client.onSessionCreated(inputId, sessionToken, channel, seq);
831         } catch (RemoteException e) {
832             Slog.e(TAG, "error in onSessionCreated", e);
833         }
834     }
835 
836     @GuardedBy("mLock")
837     @Nullable
releaseSessionLocked(IBinder sessionToken, int callingUid, int userId)838     private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
839         SessionState sessionState = null;
840         try {
841             sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
842             UserState userState = getOrCreateUserStateLocked(userId);
843             if (sessionState.session != null) {
844                 if (sessionToken == userState.mainSessionToken) {
845                     setMainLocked(sessionToken, false, callingUid, userId);
846                 }
847                 sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
848                 sessionState.session.release();
849             }
850             sessionState.currentChannel = null;
851             if (sessionState.isCurrent) {
852                 sessionState.isCurrent = false;
853                 notifyCurrentChannelInfosUpdatedLocked(userState);
854             }
855         } catch (RemoteException | SessionNotFoundException e) {
856             Slog.e(TAG, "error in releaseSession", e);
857         } finally {
858             if (sessionState != null) {
859                 sessionState.session = null;
860             }
861         }
862         removeSessionStateLocked(sessionToken, userId);
863         return sessionState;
864     }
865 
866     @GuardedBy("mLock")
removeSessionStateLocked(IBinder sessionToken, int userId)867     private void removeSessionStateLocked(IBinder sessionToken, int userId) {
868         UserState userState = getOrCreateUserStateLocked(userId);
869         if (sessionToken == userState.mainSessionToken) {
870             if (DEBUG) {
871                 Slog.d(TAG, "mainSessionToken=null");
872             }
873             userState.mainSessionToken = null;
874         }
875 
876         // Remove the session state from the global session state map of the current user.
877         SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
878 
879         if (sessionState == null) {
880             Slog.e(TAG, "sessionState null, no more remove session action!");
881             return;
882         }
883 
884         // Also remove the session token from the session token list of the current client and
885         // service.
886         ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
887         if (clientState != null) {
888             clientState.sessionTokens.remove(sessionToken);
889             if (clientState.isEmpty()) {
890                 userState.clientStateMap.remove(sessionState.client.asBinder());
891                 sessionState.client.asBinder().unlinkToDeath(clientState, 0);
892             }
893         }
894 
895         mSessionIdToSessionStateMap.remove(sessionState.sessionId);
896 
897         ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName);
898         if (serviceState != null) {
899             serviceState.sessionTokens.remove(sessionToken);
900         }
901         updateServiceConnectionLocked(sessionState.componentName, userId);
902 
903         // Log the end of watch.
904         SomeArgs args = SomeArgs.obtain();
905         args.arg1 = sessionToken;
906         args.arg2 = System.currentTimeMillis();
907         mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget();
908     }
909 
910     @GuardedBy("mLock")
setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId)911     private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
912         try {
913             SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
914             if (sessionState.hardwareSessionToken != null) {
915                 sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
916                         Process.SYSTEM_UID, userId);
917             }
918             ServiceState serviceState = getServiceStateLocked(sessionState.componentName, userId);
919             if (!serviceState.isHardware) {
920                 return;
921             }
922             ITvInputSession session = getSessionLocked(sessionState);
923             session.setMain(isMain);
924             if (sessionState.isMainSession != isMain) {
925                 UserState userState = getUserStateLocked(userId);
926                 sessionState.isMainSession = isMain;
927                 notifyCurrentChannelInfosUpdatedLocked(userState);
928             }
929         } catch (RemoteException | SessionNotFoundException e) {
930             Slog.e(TAG, "error in setMain", e);
931         }
932     }
933 
934     @GuardedBy("mLock")
notifyInputAddedLocked(UserState userState, String inputId)935     private void notifyInputAddedLocked(UserState userState, String inputId) {
936         if (DEBUG) {
937             Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")");
938         }
939         int n = userState.mCallbacks.beginBroadcast();
940         for (int i = 0; i < n; ++i) {
941             try {
942                 userState.mCallbacks.getBroadcastItem(i).onInputAdded(inputId);
943             } catch (RemoteException e) {
944                 Slog.e(TAG, "failed to report added input to callback", e);
945             }
946         }
947         userState.mCallbacks.finishBroadcast();
948     }
949 
950     @GuardedBy("mLock")
notifyInputRemovedLocked(UserState userState, String inputId)951     private void notifyInputRemovedLocked(UserState userState, String inputId) {
952         if (DEBUG) {
953             Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")");
954         }
955         int n = userState.mCallbacks.beginBroadcast();
956         for (int i = 0; i < n; ++i) {
957             try {
958                 userState.mCallbacks.getBroadcastItem(i).onInputRemoved(inputId);
959             } catch (RemoteException e) {
960                 Slog.e(TAG, "failed to report removed input to callback", e);
961             }
962         }
963         userState.mCallbacks.finishBroadcast();
964     }
965 
966     @GuardedBy("mLock")
notifyInputUpdatedLocked(UserState userState, String inputId)967     private void notifyInputUpdatedLocked(UserState userState, String inputId) {
968         if (DEBUG) {
969             Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")");
970         }
971         int n = userState.mCallbacks.beginBroadcast();
972         for (int i = 0; i < n; ++i) {
973             try {
974                 userState.mCallbacks.getBroadcastItem(i).onInputUpdated(inputId);
975             } catch (RemoteException e) {
976                 Slog.e(TAG, "failed to report updated input to callback", e);
977             }
978         }
979         userState.mCallbacks.finishBroadcast();
980     }
981 
982     @GuardedBy("mLock")
notifyInputStateChangedLocked(UserState userState, String inputId, int state, ITvInputManagerCallback targetCallback)983     private void notifyInputStateChangedLocked(UserState userState, String inputId,
984             int state, ITvInputManagerCallback targetCallback) {
985         if (DEBUG) {
986             Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId
987                     + ", state=" + state + ")");
988         }
989         if (targetCallback == null) {
990             int n = userState.mCallbacks.beginBroadcast();
991             for (int i = 0; i < n; ++i) {
992                 try {
993                     userState.mCallbacks.getBroadcastItem(i).onInputStateChanged(inputId, state);
994                 } catch (RemoteException e) {
995                     Slog.e(TAG, "failed to report state change to callback", e);
996                 }
997             }
998             userState.mCallbacks.finishBroadcast();
999         } else {
1000             try {
1001                 targetCallback.onInputStateChanged(inputId, state);
1002             } catch (RemoteException e) {
1003                 Slog.e(TAG, "failed to report state change to callback", e);
1004             }
1005         }
1006     }
1007 
1008     @GuardedBy("mLock")
notifyCurrentChannelInfosUpdatedLocked(UserState userState)1009     private void notifyCurrentChannelInfosUpdatedLocked(UserState userState) {
1010         if (DEBUG) {
1011             Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked");
1012         }
1013         int n = userState.mCallbacks.beginBroadcast();
1014         for (int i = 0; i < n; ++i) {
1015             try {
1016                 ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i);
1017                 Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback);
1018                 if (mContext.checkPermission(android.Manifest.permission.ACCESS_TUNED_INFO,
1019                         pidUid.first, pidUid.second) != PackageManager.PERMISSION_GRANTED) {
1020                     continue;
1021                 }
1022                 List<TunedInfo> infos = getCurrentTunedInfosInternalLocked(
1023                         userState, pidUid.first, pidUid.second);
1024                 callback.onCurrentTunedInfosUpdated(infos);
1025             } catch (RemoteException e) {
1026                 Slog.e(TAG, "failed to report updated current channel infos to callback", e);
1027             }
1028         }
1029         userState.mCallbacks.finishBroadcast();
1030     }
1031 
1032     @GuardedBy("mLock")
updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo)1033     private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) {
1034         if (DEBUG) {
1035             Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")");
1036         }
1037         String inputId = inputInfo.getId();
1038         TvInputState inputState = userState.inputMap.get(inputId);
1039         if (inputState == null) {
1040             Slog.e(TAG, "failed to set input info - unknown input id " + inputId);
1041             return;
1042         }
1043         inputState.info = inputInfo;
1044         inputState.uid = getInputUid(inputInfo);
1045 
1046         int n = userState.mCallbacks.beginBroadcast();
1047         for (int i = 0; i < n; ++i) {
1048             try {
1049                 userState.mCallbacks.getBroadcastItem(i).onTvInputInfoUpdated(inputInfo);
1050             } catch (RemoteException e) {
1051                 Slog.e(TAG, "failed to report updated input info to callback", e);
1052             }
1053         }
1054         userState.mCallbacks.finishBroadcast();
1055     }
1056 
1057     @GuardedBy("mLock")
setStateLocked(String inputId, int state, int userId)1058     private void setStateLocked(String inputId, int state, int userId) {
1059         UserState userState = getOrCreateUserStateLocked(userId);
1060         TvInputState inputState = userState.inputMap.get(inputId);
1061         if (inputState == null) {
1062             Slog.e(TAG, "failed to setStateLocked - unknown input id " + inputId);
1063             return;
1064         }
1065         ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
1066         int oldState = inputState.state;
1067         inputState.state = state;
1068         if (serviceState != null && serviceState.service == null
1069                 && (!serviceState.sessionTokens.isEmpty() || serviceState.isHardware)) {
1070             // We don't notify state change while reconnecting. It should remain disconnected.
1071             return;
1072         }
1073         if (oldState != state) {
1074             notifyInputStateChangedLocked(userState, inputId, state, null);
1075         }
1076     }
1077 
1078     private final class BinderService extends ITvInputManager.Stub {
1079         @Override
getTvInputList(int userId)1080         public List<TvInputInfo> getTvInputList(int userId) {
1081             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1082                     Binder.getCallingUid(), userId, "getTvInputList");
1083             final long identity = Binder.clearCallingIdentity();
1084             try {
1085                 synchronized (mLock) {
1086                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1087                     List<TvInputInfo> inputList = new ArrayList<>();
1088                     for (TvInputState state : userState.inputMap.values()) {
1089                         inputList.add(state.info);
1090                     }
1091                     return inputList;
1092                 }
1093             } finally {
1094                 Binder.restoreCallingIdentity(identity);
1095             }
1096         }
1097 
1098         @Override
getTvInputInfo(String inputId, int userId)1099         public TvInputInfo getTvInputInfo(String inputId, int userId) {
1100             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1101                     Binder.getCallingUid(), userId, "getTvInputInfo");
1102             final long identity = Binder.clearCallingIdentity();
1103             try {
1104                 synchronized (mLock) {
1105                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1106                     TvInputState state = userState.inputMap.get(inputId);
1107                     return state == null ? null : state.info;
1108                 }
1109             } finally {
1110                 Binder.restoreCallingIdentity(identity);
1111             }
1112         }
1113 
updateTvInputInfo(TvInputInfo inputInfo, int userId)1114         public void updateTvInputInfo(TvInputInfo inputInfo, int userId) {
1115             String inputInfoPackageName = inputInfo.getServiceInfo().packageName;
1116             String callingPackageName = getCallingPackageName();
1117             if (!TextUtils.equals(inputInfoPackageName, callingPackageName)
1118                     && mContext.checkCallingPermission(
1119                             android.Manifest.permission.WRITE_SECURE_SETTINGS)
1120                                     != PackageManager.PERMISSION_GRANTED) {
1121                 // Only the app owning the input and system settings are allowed to update info.
1122                 throw new IllegalArgumentException("calling package " + callingPackageName
1123                         + " is not allowed to change TvInputInfo for " + inputInfoPackageName);
1124             }
1125 
1126             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1127                     Binder.getCallingUid(), userId, "updateTvInputInfo");
1128             final long identity = Binder.clearCallingIdentity();
1129             try {
1130                 synchronized (mLock) {
1131                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1132                     updateTvInputInfoLocked(userState, inputInfo);
1133                 }
1134             } finally {
1135                 Binder.restoreCallingIdentity(identity);
1136             }
1137         }
1138 
getCallingPackageName()1139         private String getCallingPackageName() {
1140             final String[] packages = mContext.getPackageManager().getPackagesForUid(
1141                     Binder.getCallingUid());
1142             if (packages != null && packages.length > 0) {
1143                 return packages[0];
1144             }
1145             return "unknown";
1146         }
1147 
1148         @Override
getTvInputState(String inputId, int userId)1149         public int getTvInputState(String inputId, int userId) {
1150             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1151                     Binder.getCallingUid(), userId, "getTvInputState");
1152             final long identity = Binder.clearCallingIdentity();
1153             try {
1154                 synchronized (mLock) {
1155                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1156                     TvInputState state = userState.inputMap.get(inputId);
1157                     return state == null ? INPUT_STATE_CONNECTED : state.state;
1158                 }
1159             } finally {
1160                 Binder.restoreCallingIdentity(identity);
1161             }
1162         }
1163 
1164         @Override
getTvContentRatingSystemList(int userId)1165         public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) {
1166             if (mContext.checkCallingPermission(
1167                     android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS)
1168                     != PackageManager.PERMISSION_GRANTED) {
1169                 throw new SecurityException(
1170                         "The caller does not have permission to read content rating systems");
1171             }
1172             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1173                     Binder.getCallingUid(), userId, "getTvContentRatingSystemList");
1174             final long identity = Binder.clearCallingIdentity();
1175             try {
1176                 synchronized (mLock) {
1177                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1178                     return userState.contentRatingSystemList;
1179                 }
1180             } finally {
1181                 Binder.restoreCallingIdentity(identity);
1182             }
1183         }
1184 
1185         @Override
sendTvInputNotifyIntent(Intent intent, int userId)1186         public void sendTvInputNotifyIntent(Intent intent, int userId) {
1187             if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
1188                     != PackageManager.PERMISSION_GRANTED) {
1189                 throw new SecurityException("The caller: " + getCallingPackageName()
1190                         + " doesn't have permission: "
1191                         + android.Manifest.permission.NOTIFY_TV_INPUTS);
1192             }
1193             if (TextUtils.isEmpty(intent.getPackage())) {
1194                 throw new IllegalArgumentException("Must specify package name to notify.");
1195             }
1196             switch (intent.getAction()) {
1197                 case TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
1198                     if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
1199                         throw new IllegalArgumentException("Invalid preview program ID.");
1200                     }
1201                     break;
1202                 case TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
1203                     if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
1204                         throw new IllegalArgumentException("Invalid watch next program ID.");
1205                     }
1206                     break;
1207                 case TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
1208                     if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
1209                         throw new IllegalArgumentException("Invalid preview program ID.");
1210                     }
1211                     if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
1212                         throw new IllegalArgumentException("Invalid watch next program ID.");
1213                     }
1214                     break;
1215                 default:
1216                     throw new IllegalArgumentException("Invalid TV input notifying action: "
1217                             + intent.getAction());
1218             }
1219             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1220                     Binder.getCallingUid(), userId, "sendTvInputNotifyIntent");
1221             final long identity = Binder.clearCallingIdentity();
1222             try {
1223                 getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
1224             } finally {
1225                 Binder.restoreCallingIdentity(identity);
1226             }
1227         }
1228 
1229         @Override
registerCallback(final ITvInputManagerCallback callback, int userId)1230         public void registerCallback(final ITvInputManagerCallback callback, int userId) {
1231             int callingPid = Binder.getCallingPid();
1232             int callingUid = Binder.getCallingUid();
1233             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
1234                     "registerCallback");
1235             final long identity = Binder.clearCallingIdentity();
1236             try {
1237                 synchronized (mLock) {
1238                     final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1239                     if (!userState.mCallbacks.register(callback)) {
1240                         Slog.e(TAG, "client process has already died");
1241                     } else {
1242                         userState.callbackPidUidMap.put(
1243                                 callback, Pair.create(callingPid, callingUid));
1244                     }
1245                 }
1246             } finally {
1247                 Binder.restoreCallingIdentity(identity);
1248             }
1249         }
1250 
1251         @Override
unregisterCallback(ITvInputManagerCallback callback, int userId)1252         public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
1253             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1254                     Binder.getCallingUid(), userId, "unregisterCallback");
1255             final long identity = Binder.clearCallingIdentity();
1256             try {
1257                 synchronized (mLock) {
1258                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1259                     userState.mCallbacks.unregister(callback);
1260                     userState.callbackPidUidMap.remove(callback);
1261                 }
1262             } finally {
1263                 Binder.restoreCallingIdentity(identity);
1264             }
1265         }
1266 
1267         @Override
isParentalControlsEnabled(int userId)1268         public boolean isParentalControlsEnabled(int userId) {
1269             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1270                     Binder.getCallingUid(), userId, "isParentalControlsEnabled");
1271             final long identity = Binder.clearCallingIdentity();
1272             try {
1273                 synchronized (mLock) {
1274                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1275                     return userState.persistentDataStore.isParentalControlsEnabled();
1276                 }
1277             } finally {
1278                 Binder.restoreCallingIdentity(identity);
1279             }
1280         }
1281 
1282         @Override
setParentalControlsEnabled(boolean enabled, int userId)1283         public void setParentalControlsEnabled(boolean enabled, int userId) {
1284             ensureParentalControlsPermission();
1285             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1286                     Binder.getCallingUid(), userId, "setParentalControlsEnabled");
1287             final long identity = Binder.clearCallingIdentity();
1288             try {
1289                 synchronized (mLock) {
1290                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1291                     userState.persistentDataStore.setParentalControlsEnabled(enabled);
1292                 }
1293             } finally {
1294                 Binder.restoreCallingIdentity(identity);
1295             }
1296         }
1297 
1298         @Override
isRatingBlocked(String rating, int userId)1299         public boolean isRatingBlocked(String rating, int userId) {
1300             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1301                     Binder.getCallingUid(), userId, "isRatingBlocked");
1302             final long identity = Binder.clearCallingIdentity();
1303             try {
1304                 synchronized (mLock) {
1305                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1306                     return userState.persistentDataStore.isRatingBlocked(
1307                             TvContentRating.unflattenFromString(rating));
1308                 }
1309             } finally {
1310                 Binder.restoreCallingIdentity(identity);
1311             }
1312         }
1313 
1314         @Override
getBlockedRatings(int userId)1315         public List<String> getBlockedRatings(int userId) {
1316             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1317                     Binder.getCallingUid(), userId, "getBlockedRatings");
1318             final long identity = Binder.clearCallingIdentity();
1319             try {
1320                 synchronized (mLock) {
1321                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1322                     List<String> ratings = new ArrayList<>();
1323                     for (TvContentRating rating
1324                             : userState.persistentDataStore.getBlockedRatings()) {
1325                         ratings.add(rating.flattenToString());
1326                     }
1327                     return ratings;
1328                 }
1329             } finally {
1330                 Binder.restoreCallingIdentity(identity);
1331             }
1332         }
1333 
1334         @Override
addBlockedRating(String rating, int userId)1335         public void addBlockedRating(String rating, int userId) {
1336             ensureParentalControlsPermission();
1337             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1338                     Binder.getCallingUid(), userId, "addBlockedRating");
1339             final long identity = Binder.clearCallingIdentity();
1340             try {
1341                 synchronized (mLock) {
1342                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1343                     userState.persistentDataStore.addBlockedRating(
1344                             TvContentRating.unflattenFromString(rating));
1345                 }
1346             } finally {
1347                 Binder.restoreCallingIdentity(identity);
1348             }
1349         }
1350 
1351         @Override
removeBlockedRating(String rating, int userId)1352         public void removeBlockedRating(String rating, int userId) {
1353             ensureParentalControlsPermission();
1354             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1355                     Binder.getCallingUid(), userId, "removeBlockedRating");
1356             final long identity = Binder.clearCallingIdentity();
1357             try {
1358                 synchronized (mLock) {
1359                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1360                     userState.persistentDataStore.removeBlockedRating(
1361                             TvContentRating.unflattenFromString(rating));
1362                 }
1363             } finally {
1364                 Binder.restoreCallingIdentity(identity);
1365             }
1366         }
1367 
ensureParentalControlsPermission()1368         private void ensureParentalControlsPermission() {
1369             if (mContext.checkCallingPermission(
1370                     android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
1371                     != PackageManager.PERMISSION_GRANTED) {
1372                 throw new SecurityException(
1373                         "The caller does not have parental controls permission");
1374             }
1375         }
1376 
1377         @Override
createSession(final ITvInputClient client, final String inputId, boolean isRecordingSession, int seq, int userId)1378         public void createSession(final ITvInputClient client, final String inputId,
1379                 boolean isRecordingSession, int seq, int userId) {
1380             final int callingUid = Binder.getCallingUid();
1381             final int callingPid = Binder.getCallingPid();
1382             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
1383                     userId, "createSession");
1384             final long identity = Binder.clearCallingIdentity();
1385             /**
1386              * A randomly generated id for this this session.
1387              *
1388              * <p>This field contains no user or device reference and is large enough to be
1389              * effectively globally unique.
1390              *
1391              * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
1392              * Inspect the code at:
1393              *
1394              * <ul>
1395              *   <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
1396              *   <li>{@link #logTuneStateChanged}
1397              *   <li>{@link TvInputManagerService.BinderService#createSession}
1398              *   <li>{@link SessionState#sessionId}
1399              * </ul>
1400              */
1401             String uniqueSessionId = UUID.randomUUID().toString();
1402             try {
1403                 synchronized (mLock) {
1404                     if (userId != mCurrentUserId && !mRunningProfiles.contains(userId)
1405                             && !isRecordingSession) {
1406                         // Only current user and its running profiles can create
1407                         // non-recording sessions.
1408                         // Let the client get onConnectionFailed callback for this case.
1409                         sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1410                         return;
1411                     }
1412                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1413                     TvInputState inputState = userState.inputMap.get(inputId);
1414                     if (inputState == null) {
1415                         Slog.w(TAG, "Failed to find input state for inputId=" + inputId);
1416                         sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1417                         return;
1418                     }
1419                     TvInputInfo info = inputState.info;
1420                     ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
1421                     if (serviceState == null) {
1422                         int tisUid = PackageManager.getApplicationInfoAsUserCached(
1423                                 info.getComponent().getPackageName(), 0, resolvedUserId).uid;
1424                         serviceState = new ServiceState(info.getComponent(), resolvedUserId);
1425                         userState.serviceStateMap.put(info.getComponent(), serviceState);
1426                     }
1427                     // Send a null token immediately while reconnecting.
1428                     if (serviceState.reconnecting) {
1429                         sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1430                         return;
1431                     }
1432 
1433                     // Create a new session token and a session state.
1434                     IBinder sessionToken = new Binder();
1435                     SessionState sessionState = new SessionState(sessionToken, info.getId(),
1436                             info.getComponent(), isRecordingSession, client, seq, callingUid,
1437                             callingPid, resolvedUserId, uniqueSessionId);
1438 
1439                     // Add them to the global session state map of the current user.
1440                     userState.sessionStateMap.put(sessionToken, sessionState);
1441 
1442                     // Map the session id to the sessionStateMap in the user state
1443                     mSessionIdToSessionStateMap.put(uniqueSessionId, sessionState);
1444 
1445                     // Also, add them to the session state map of the current service.
1446                     serviceState.sessionTokens.add(sessionToken);
1447 
1448                     if (serviceState.service != null) {
1449                         if (!createSessionInternalLocked(serviceState.service, sessionToken,
1450                                 resolvedUserId)) {
1451                             removeSessionStateLocked(sessionToken, resolvedUserId);
1452                         }
1453                     } else {
1454                         updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
1455                     }
1456                     logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED,
1457                             sessionState, inputState);
1458                 }
1459             } finally {
1460                 Binder.restoreCallingIdentity(identity);
1461             }
1462         }
1463 
1464         @Override
releaseSession(IBinder sessionToken, int userId)1465         public void releaseSession(IBinder sessionToken, int userId) {
1466             if (DEBUG) {
1467                 Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
1468             }
1469             final int callingUid = Binder.getCallingUid();
1470             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1471                     userId, "releaseSession");
1472             final long identity = Binder.clearCallingIdentity();
1473             try {
1474                 SessionState sessionState = null;
1475                 UserState userState = null;
1476                 synchronized (mLock) {
1477                     sessionState = releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
1478                     userState = getUserStateLocked(userId);
1479                 }
1480                 if (sessionState != null) {
1481                     TvInputState tvInputState = TvInputManagerService.getTvInputState(sessionState,
1482                             userState);
1483                     logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__RELEASED,
1484                             sessionState, tvInputState);
1485                 }
1486             } finally {
1487                 Binder.restoreCallingIdentity(identity);
1488             }
1489         }
1490 
1491         @Override
setMainSession(IBinder sessionToken, int userId)1492         public void setMainSession(IBinder sessionToken, int userId) {
1493             if (mContext.checkCallingPermission(
1494                     android.Manifest.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE)
1495                     != PackageManager.PERMISSION_GRANTED) {
1496                 throw new SecurityException(
1497                         "The caller does not have CHANGE_HDMI_CEC_ACTIVE_SOURCE permission");
1498             }
1499             if (DEBUG) {
1500                 Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")");
1501             }
1502             final int callingUid = Binder.getCallingUid();
1503             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1504                     userId, "setMainSession");
1505             final long identity = Binder.clearCallingIdentity();
1506             try {
1507                 synchronized (mLock) {
1508                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1509                     if (userState.mainSessionToken == sessionToken) {
1510                         return;
1511                     }
1512                     if (DEBUG) {
1513                         Slog.d(TAG, "mainSessionToken=" + sessionToken);
1514                     }
1515                     IBinder oldMainSessionToken = userState.mainSessionToken;
1516                     userState.mainSessionToken = sessionToken;
1517 
1518                     // Inform the new main session first.
1519                     // See {@link TvInputService.Session#onSetMain}.
1520                     if (sessionToken != null) {
1521                         setMainLocked(sessionToken, true, callingUid, userId);
1522                     }
1523                     if (oldMainSessionToken != null) {
1524                         setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId);
1525                     }
1526                 }
1527             } finally {
1528                 Binder.restoreCallingIdentity(identity);
1529             }
1530         }
1531 
1532         @Override
setSurface(IBinder sessionToken, Surface surface, int userId)1533         public void setSurface(IBinder sessionToken, Surface surface, int userId) {
1534             final int callingUid = Binder.getCallingUid();
1535             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1536                     userId, "setSurface");
1537             final long identity = Binder.clearCallingIdentity();
1538             SessionState sessionState = null;
1539             UserState userState = null;
1540             try {
1541                 synchronized (mLock) {
1542                     try {
1543                         userState = getUserStateLocked(userId);
1544                         sessionState = getSessionStateLocked(sessionToken, callingUid,
1545                                 resolvedUserId);
1546                         if (sessionState.hardwareSessionToken == null) {
1547                             getSessionLocked(sessionState).setSurface(surface);
1548                         } else {
1549                             getSessionLocked(sessionState.hardwareSessionToken,
1550                                     Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
1551                         }
1552                         boolean isVisible = (surface == null);
1553                         if (sessionState.isVisible != isVisible) {
1554                             sessionState.isVisible = isVisible;
1555                             notifyCurrentChannelInfosUpdatedLocked(userState);
1556                         }
1557                     } catch (RemoteException | SessionNotFoundException e) {
1558                         Slog.e(TAG, "error in setSurface", e);
1559                     }
1560                 }
1561             } finally {
1562                 if (surface != null) {
1563                     // surface is not used in TvInputManagerService.
1564                     surface.release();
1565                 }
1566                 if (sessionState != null) {
1567                     int state = surface == null
1568                             ?
1569                             FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED
1570                             : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED;
1571                     logTuneStateChanged(state, sessionState,
1572                             TvInputManagerService.getTvInputState(sessionState, userState));
1573                 }
1574                 Binder.restoreCallingIdentity(identity);
1575             }
1576         }
1577 
1578         @Override
dispatchSurfaceChanged(IBinder sessionToken, int format, int width, int height, int userId)1579         public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
1580                 int height, int userId) {
1581             final int callingUid = Binder.getCallingUid();
1582             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1583                     userId, "dispatchSurfaceChanged");
1584             final long identity = Binder.clearCallingIdentity();
1585             try {
1586                 synchronized (mLock) {
1587                     try {
1588                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1589                                 resolvedUserId);
1590                         getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
1591                                 height);
1592                         if (sessionState.hardwareSessionToken != null) {
1593                             getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
1594                                     resolvedUserId).dispatchSurfaceChanged(format, width, height);
1595                         }
1596                     } catch (RemoteException | SessionNotFoundException e) {
1597                         Slog.e(TAG, "error in dispatchSurfaceChanged", e);
1598                     }
1599                 }
1600             } finally {
1601                 Binder.restoreCallingIdentity(identity);
1602             }
1603         }
1604 
1605         @Override
setVolume(IBinder sessionToken, float volume, int userId)1606         public void setVolume(IBinder sessionToken, float volume, int userId) {
1607             final float REMOTE_VOLUME_ON = 1.0f;
1608             final float REMOTE_VOLUME_OFF = 0f;
1609             final int callingUid = Binder.getCallingUid();
1610             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1611                     userId, "setVolume");
1612             final long identity = Binder.clearCallingIdentity();
1613             try {
1614                 synchronized (mLock) {
1615                     try {
1616                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1617                                 resolvedUserId);
1618                         getSessionLocked(sessionState).setVolume(volume);
1619                         if (sessionState.hardwareSessionToken != null) {
1620                             // Here, we let the hardware session know only whether volume is on or
1621                             // off to prevent that the volume is controlled in the both side.
1622                             getSessionLocked(sessionState.hardwareSessionToken,
1623                                     Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
1624                                             ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
1625                         }
1626                     } catch (RemoteException | SessionNotFoundException e) {
1627                         Slog.e(TAG, "error in setVolume", e);
1628                     }
1629                 }
1630             } finally {
1631                 Binder.restoreCallingIdentity(identity);
1632             }
1633         }
1634 
1635         @Override
tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId)1636         public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
1637             final int callingUid = Binder.getCallingUid();
1638             final int callingPid = Binder.getCallingPid();
1639             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "tune");
1640             final long identity = Binder.clearCallingIdentity();
1641             try {
1642                 synchronized (mLock) {
1643                     try {
1644                         getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
1645                                 channelUri, params);
1646                         UserState userState = getOrCreateUserStateLocked(resolvedUserId);
1647                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1648                                 userState);
1649                         if (!sessionState.isCurrent
1650                                 || !Objects.equals(sessionState.currentChannel, channelUri)) {
1651                             sessionState.isCurrent = true;
1652                             sessionState.currentChannel = channelUri;
1653                             notifyCurrentChannelInfosUpdatedLocked(userState);
1654                         }
1655                         if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
1656                             // Do not log the watch history for passthrough inputs.
1657                             return;
1658                         }
1659 
1660                         if (sessionState.isRecordingSession) {
1661                             return;
1662                         }
1663 
1664                         logTuneStateChanged(
1665                                 FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED,
1666                                 sessionState,
1667                                 TvInputManagerService.getTvInputState(sessionState, userState));
1668                         // Log the start of watch.
1669                         SomeArgs args = SomeArgs.obtain();
1670                         args.arg1 = sessionState.componentName.getPackageName();
1671                         args.arg2 = System.currentTimeMillis();
1672                         args.arg3 = ContentUris.parseId(channelUri);
1673                         args.arg4 = params;
1674                         args.arg5 = sessionToken;
1675                         mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args)
1676                                 .sendToTarget();
1677                     } catch (RemoteException | SessionNotFoundException e) {
1678                         Slog.e(TAG, "error in tune", e);
1679                     }
1680                 }
1681             } finally {
1682                 Binder.restoreCallingIdentity(identity);
1683             }
1684         }
1685 
1686         @Override
unblockContent( IBinder sessionToken, String unblockedRating, int userId)1687         public void unblockContent(
1688                 IBinder sessionToken, String unblockedRating, int userId) {
1689             ensureParentalControlsPermission();
1690             final int callingUid = Binder.getCallingUid();
1691             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1692                     userId, "unblockContent");
1693             final long identity = Binder.clearCallingIdentity();
1694             try {
1695                 synchronized (mLock) {
1696                     try {
1697                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1698                                 .unblockContent(unblockedRating);
1699                     } catch (RemoteException | SessionNotFoundException e) {
1700                         Slog.e(TAG, "error in unblockContent", e);
1701                     }
1702                 }
1703             } finally {
1704                 Binder.restoreCallingIdentity(identity);
1705             }
1706         }
1707 
1708         @Override
setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId)1709         public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
1710             final int callingUid = Binder.getCallingUid();
1711             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1712                     userId, "setCaptionEnabled");
1713             final long identity = Binder.clearCallingIdentity();
1714             try {
1715                 synchronized (mLock) {
1716                     try {
1717                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1718                                 .setCaptionEnabled(enabled);
1719                     } catch (RemoteException | SessionNotFoundException e) {
1720                         Slog.e(TAG, "error in setCaptionEnabled", e);
1721                     }
1722                 }
1723             } finally {
1724                 Binder.restoreCallingIdentity(identity);
1725             }
1726         }
1727 
1728         @Override
selectTrack(IBinder sessionToken, int type, String trackId, int userId)1729         public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
1730             final int callingUid = Binder.getCallingUid();
1731             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1732                     userId, "selectTrack");
1733             final long identity = Binder.clearCallingIdentity();
1734             try {
1735                 synchronized (mLock) {
1736                     try {
1737                         getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
1738                                 type, trackId);
1739                     } catch (RemoteException | SessionNotFoundException e) {
1740                         Slog.e(TAG, "error in selectTrack", e);
1741                     }
1742                 }
1743             } finally {
1744                 Binder.restoreCallingIdentity(identity);
1745             }
1746         }
1747 
1748         @Override
sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data, int userId)1749         public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
1750                 int userId) {
1751             final int callingUid = Binder.getCallingUid();
1752             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1753                     userId, "sendAppPrivateCommand");
1754             final long identity = Binder.clearCallingIdentity();
1755             try {
1756                 synchronized (mLock) {
1757                     try {
1758                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1759                                 .appPrivateCommand(command, data);
1760                     } catch (RemoteException | SessionNotFoundException e) {
1761                         Slog.e(TAG, "error in appPrivateCommand", e);
1762                     }
1763                 }
1764             } finally {
1765                 Binder.restoreCallingIdentity(identity);
1766             }
1767         }
1768 
1769         @Override
createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame, int userId)1770         public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
1771                 int userId) {
1772             final int callingUid = Binder.getCallingUid();
1773             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1774                     userId, "createOverlayView");
1775             final long identity = Binder.clearCallingIdentity();
1776             try {
1777                 synchronized (mLock) {
1778                     try {
1779                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1780                                 .createOverlayView(windowToken, frame);
1781                     } catch (RemoteException | SessionNotFoundException e) {
1782                         Slog.e(TAG, "error in createOverlayView", e);
1783                     }
1784                 }
1785             } finally {
1786                 Binder.restoreCallingIdentity(identity);
1787             }
1788         }
1789 
1790         @Override
relayoutOverlayView(IBinder sessionToken, Rect frame, int userId)1791         public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
1792             final int callingUid = Binder.getCallingUid();
1793             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1794                     userId, "relayoutOverlayView");
1795             final long identity = Binder.clearCallingIdentity();
1796             try {
1797                 synchronized (mLock) {
1798                     try {
1799                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1800                                 .relayoutOverlayView(frame);
1801                     } catch (RemoteException | SessionNotFoundException e) {
1802                         Slog.e(TAG, "error in relayoutOverlayView", e);
1803                     }
1804                 }
1805             } finally {
1806                 Binder.restoreCallingIdentity(identity);
1807             }
1808         }
1809 
1810         @Override
removeOverlayView(IBinder sessionToken, int userId)1811         public void removeOverlayView(IBinder sessionToken, int userId) {
1812             final int callingUid = Binder.getCallingUid();
1813             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1814                     userId, "removeOverlayView");
1815             final long identity = Binder.clearCallingIdentity();
1816             try {
1817                 synchronized (mLock) {
1818                     try {
1819                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1820                                 .removeOverlayView();
1821                     } catch (RemoteException | SessionNotFoundException e) {
1822                         Slog.e(TAG, "error in removeOverlayView", e);
1823                     }
1824                 }
1825             } finally {
1826                 Binder.restoreCallingIdentity(identity);
1827             }
1828         }
1829 
1830         @Override
timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId)1831         public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
1832             final int callingUid = Binder.getCallingUid();
1833             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1834                     userId, "timeShiftPlay");
1835             final long identity = Binder.clearCallingIdentity();
1836             try {
1837                 synchronized (mLock) {
1838                     try {
1839                         getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay(
1840                                 recordedProgramUri);
1841                     } catch (RemoteException | SessionNotFoundException e) {
1842                         Slog.e(TAG, "error in timeShiftPlay", e);
1843                     }
1844                 }
1845             } finally {
1846                 Binder.restoreCallingIdentity(identity);
1847             }
1848         }
1849 
1850         @Override
timeShiftPause(IBinder sessionToken, int userId)1851         public void timeShiftPause(IBinder sessionToken, int userId) {
1852             final int callingUid = Binder.getCallingUid();
1853             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1854                     userId, "timeShiftPause");
1855             final long identity = Binder.clearCallingIdentity();
1856             try {
1857                 synchronized (mLock) {
1858                     try {
1859                         getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause();
1860                     } catch (RemoteException | SessionNotFoundException e) {
1861                         Slog.e(TAG, "error in timeShiftPause", e);
1862                     }
1863                 }
1864             } finally {
1865                 Binder.restoreCallingIdentity(identity);
1866             }
1867         }
1868 
1869         @Override
timeShiftResume(IBinder sessionToken, int userId)1870         public void timeShiftResume(IBinder sessionToken, int userId) {
1871             final int callingUid = Binder.getCallingUid();
1872             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1873                     userId, "timeShiftResume");
1874             final long identity = Binder.clearCallingIdentity();
1875             try {
1876                 synchronized (mLock) {
1877                     try {
1878                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1879                                 .timeShiftResume();
1880                     } catch (RemoteException | SessionNotFoundException e) {
1881                         Slog.e(TAG, "error in timeShiftResume", e);
1882                     }
1883                 }
1884             } finally {
1885                 Binder.restoreCallingIdentity(identity);
1886             }
1887         }
1888 
1889         @Override
timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId)1890         public void timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId) {
1891             final int callingUid = Binder.getCallingUid();
1892             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1893                     userId, "timeShiftSeekTo");
1894             final long identity = Binder.clearCallingIdentity();
1895             try {
1896                 synchronized (mLock) {
1897                     try {
1898                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1899                                 .timeShiftSeekTo(timeMs);
1900                     } catch (RemoteException | SessionNotFoundException e) {
1901                         Slog.e(TAG, "error in timeShiftSeekTo", e);
1902                     }
1903                 }
1904             } finally {
1905                 Binder.restoreCallingIdentity(identity);
1906             }
1907         }
1908 
1909         @Override
timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params, int userId)1910         public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params,
1911                 int userId) {
1912             final int callingUid = Binder.getCallingUid();
1913             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1914                     userId, "timeShiftSetPlaybackParams");
1915             final long identity = Binder.clearCallingIdentity();
1916             try {
1917                 synchronized (mLock) {
1918                     try {
1919                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1920                                 .timeShiftSetPlaybackParams(params);
1921                     } catch (RemoteException | SessionNotFoundException e) {
1922                         Slog.e(TAG, "error in timeShiftSetPlaybackParams", e);
1923                     }
1924                 }
1925             } finally {
1926                 Binder.restoreCallingIdentity(identity);
1927             }
1928         }
1929 
1930         @Override
timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable, int userId)1931         public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable,
1932                 int userId) {
1933             final int callingUid = Binder.getCallingUid();
1934             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1935                     userId, "timeShiftEnablePositionTracking");
1936             final long identity = Binder.clearCallingIdentity();
1937             try {
1938                 synchronized (mLock) {
1939                     try {
1940                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
1941                                 .timeShiftEnablePositionTracking(enable);
1942                     } catch (RemoteException | SessionNotFoundException e) {
1943                         Slog.e(TAG, "error in timeShiftEnablePositionTracking", e);
1944                     }
1945                 }
1946             } finally {
1947                 Binder.restoreCallingIdentity(identity);
1948             }
1949         }
1950 
1951         @Override
startRecording(IBinder sessionToken, @Nullable Uri programUri, @Nullable Bundle params, int userId)1952         public void startRecording(IBinder sessionToken, @Nullable Uri programUri,
1953                 @Nullable Bundle params, int userId) {
1954             final int callingUid = Binder.getCallingUid();
1955             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1956                     userId, "startRecording");
1957             final long identity = Binder.clearCallingIdentity();
1958             try {
1959                 synchronized (mLock) {
1960                     try {
1961                         getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording(
1962                                 programUri, params);
1963                     } catch (RemoteException | SessionNotFoundException e) {
1964                         Slog.e(TAG, "error in startRecording", e);
1965                     }
1966                 }
1967             } finally {
1968                 Binder.restoreCallingIdentity(identity);
1969             }
1970         }
1971 
1972         @Override
stopRecording(IBinder sessionToken, int userId)1973         public void stopRecording(IBinder sessionToken, int userId) {
1974             final int callingUid = Binder.getCallingUid();
1975             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1976                     userId, "stopRecording");
1977             final long identity = Binder.clearCallingIdentity();
1978             try {
1979                 synchronized (mLock) {
1980                     try {
1981                         getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording();
1982                     } catch (RemoteException | SessionNotFoundException e) {
1983                         Slog.e(TAG, "error in stopRecording", e);
1984                     }
1985                 }
1986             } finally {
1987                 Binder.restoreCallingIdentity(identity);
1988             }
1989         }
1990 
1991         @Override
pauseRecording(IBinder sessionToken, @NonNull Bundle params, int userId)1992         public void pauseRecording(IBinder sessionToken, @NonNull Bundle params, int userId) {
1993             final int callingUid = Binder.getCallingUid();
1994             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1995                     userId, "pauseRecording");
1996             final long identity = Binder.clearCallingIdentity();
1997             try {
1998                 synchronized (mLock) {
1999                     try {
2000                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
2001                                 .pauseRecording(params);
2002                     } catch (RemoteException | SessionNotFoundException e) {
2003                         Slog.e(TAG, "error in pauseRecording", e);
2004                     }
2005                 }
2006             } finally {
2007                 Binder.restoreCallingIdentity(identity);
2008             }
2009         }
2010 
2011         @Override
resumeRecording(IBinder sessionToken, @NonNull Bundle params, int userId)2012         public void resumeRecording(IBinder sessionToken, @NonNull Bundle params, int userId) {
2013             final int callingUid = Binder.getCallingUid();
2014             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
2015                     userId, "resumeRecording");
2016             final long identity = Binder.clearCallingIdentity();
2017             try {
2018                 synchronized (mLock) {
2019                     try {
2020                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
2021                                 .resumeRecording(params);
2022                     } catch (RemoteException | SessionNotFoundException e) {
2023                         Slog.e(TAG, "error in resumeRecording", e);
2024                     }
2025                 }
2026             } finally {
2027                 Binder.restoreCallingIdentity(identity);
2028             }
2029         }
2030 
2031         @Override
getHardwareList()2032         public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
2033             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2034                     != PackageManager.PERMISSION_GRANTED) {
2035                 return null;
2036             }
2037 
2038             final long identity = Binder.clearCallingIdentity();
2039             try {
2040                 return mTvInputHardwareManager.getHardwareList();
2041             } finally {
2042                 Binder.restoreCallingIdentity(identity);
2043             }
2044         }
2045 
2046         @Override
acquireTvInputHardware(int deviceId, ITvInputHardwareCallback callback, TvInputInfo info, int userId, String tvInputSessionId, @TvInputService.PriorityHintUseCaseType int priorityHint)2047         public ITvInputHardware acquireTvInputHardware(int deviceId,
2048                 ITvInputHardwareCallback callback, TvInputInfo info, int userId,
2049                 String tvInputSessionId,
2050                 @TvInputService.PriorityHintUseCaseType int priorityHint) throws RemoteException {
2051             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2052                     != PackageManager.PERMISSION_GRANTED) {
2053                 return null;
2054             }
2055 
2056             final long identity = Binder.clearCallingIdentity();
2057             final int callingUid = Binder.getCallingUid();
2058             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
2059                     userId, "acquireTvInputHardware");
2060             try {
2061                 return mTvInputHardwareManager.acquireHardware(
2062                         deviceId, callback, info, callingUid, resolvedUserId,
2063                         tvInputSessionId, priorityHint);
2064             } finally {
2065                 Binder.restoreCallingIdentity(identity);
2066             }
2067         }
2068 
2069         @Override
releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)2070         public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
2071                 throws RemoteException {
2072             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2073                     != PackageManager.PERMISSION_GRANTED) {
2074                 return;
2075             }
2076 
2077             final long identity = Binder.clearCallingIdentity();
2078             final int callingUid = Binder.getCallingUid();
2079             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
2080                     userId, "releaseTvInputHardware");
2081             try {
2082                 mTvInputHardwareManager.releaseHardware(
2083                         deviceId, hardware, callingUid, resolvedUserId);
2084             } finally {
2085                 Binder.restoreCallingIdentity(identity);
2086             }
2087         }
2088 
2089         @Override
getDvbDeviceList()2090         public List<DvbDeviceInfo> getDvbDeviceList() throws RemoteException {
2091             if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
2092                     != PackageManager.PERMISSION_GRANTED) {
2093                 throw new SecurityException("Requires DVB_DEVICE permission");
2094             }
2095 
2096             final long identity = Binder.clearCallingIdentity();
2097             try {
2098                 // Pattern1: /dev/dvb%d.frontend%d
2099                 ArrayList<DvbDeviceInfo> deviceInfosFromPattern1 = new ArrayList<>();
2100                 File devDirectory = new File("/dev");
2101                 boolean dvbDirectoryFound = false;
2102                 for (String fileName : devDirectory.list()) {
2103                     Matcher matcher = sFrontEndDevicePattern.matcher(fileName);
2104                     if (matcher.find()) {
2105                         int adapterId = Integer.parseInt(matcher.group(1));
2106                         int deviceId = Integer.parseInt(matcher.group(2));
2107                         deviceInfosFromPattern1.add(new DvbDeviceInfo(adapterId, deviceId));
2108                     }
2109                     if (TextUtils.equals("dvb", fileName)) {
2110                         dvbDirectoryFound = true;
2111                     }
2112                 }
2113                 if (!dvbDirectoryFound) {
2114                     return Collections.unmodifiableList(deviceInfosFromPattern1);
2115                 }
2116                 File dvbDirectory = new File(DVB_DIRECTORY);
2117                 // Pattern2: /dev/dvb/adapter%d/frontend%d
2118                 ArrayList<DvbDeviceInfo> deviceInfosFromPattern2 = new ArrayList<>();
2119                 for (String fileNameInDvb : dvbDirectory.list()) {
2120                     Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
2121                     if (adapterMatcher.find()) {
2122                         int adapterId = Integer.parseInt(adapterMatcher.group(1));
2123                         File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
2124                         for (String fileNameInAdapter : adapterDirectory.list()) {
2125                             Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
2126                                     fileNameInAdapter);
2127                             if (frontendMatcher.find()) {
2128                                 int deviceId = Integer.parseInt(frontendMatcher.group(1));
2129                                 deviceInfosFromPattern2.add(
2130                                         new DvbDeviceInfo(adapterId, deviceId));
2131                             }
2132                         }
2133                     }
2134                 }
2135                 return deviceInfosFromPattern2.isEmpty()
2136                         ? Collections.unmodifiableList(deviceInfosFromPattern1)
2137                         : Collections.unmodifiableList(deviceInfosFromPattern2);
2138             } finally {
2139                 Binder.restoreCallingIdentity(identity);
2140             }
2141         }
2142 
2143         @Override
openDvbDevice(DvbDeviceInfo info, @TvInputManager.DvbDeviceType int deviceType)2144         public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info,
2145                 @TvInputManager.DvbDeviceType int deviceType)  throws RemoteException {
2146             if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
2147                     != PackageManager.PERMISSION_GRANTED) {
2148                 throw new SecurityException("Requires DVB_DEVICE permission");
2149             }
2150 
2151             File devDirectory = new File("/dev");
2152             boolean dvbDeviceFound = false;
2153             for (String fileName : devDirectory.list()) {
2154                 if (TextUtils.equals("dvb", fileName)) {
2155                     File dvbDirectory = new File(DVB_DIRECTORY);
2156                     for (String fileNameInDvb : dvbDirectory.list()) {
2157                         Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
2158                         if (adapterMatcher.find()) {
2159                             File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
2160                             for (String fileNameInAdapter : adapterDirectory.list()) {
2161                                 Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
2162                                         fileNameInAdapter);
2163                                 if (frontendMatcher.find()) {
2164                                     dvbDeviceFound = true;
2165                                     break;
2166                                 }
2167                             }
2168                         }
2169                         if (dvbDeviceFound) {
2170                             break;
2171                         }
2172                     }
2173                 }
2174                 if (dvbDeviceFound) {
2175                     break;
2176                 }
2177             }
2178 
2179             final long identity = Binder.clearCallingIdentity();
2180             try {
2181                 String deviceFileName;
2182                 switch (deviceType) {
2183                     case TvInputManager.DVB_DEVICE_DEMUX:
2184                         deviceFileName = String.format(dvbDeviceFound
2185                                 ? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d",
2186                                 info.getAdapterId(), info.getDeviceId());
2187                         break;
2188                     case TvInputManager.DVB_DEVICE_DVR:
2189                         deviceFileName = String.format(dvbDeviceFound
2190                                 ? "/dev/dvb/adapter%d/dvr%d" : "/dev/dvb%d.dvr%d",
2191                                 info.getAdapterId(), info.getDeviceId());
2192                         break;
2193                     case TvInputManager.DVB_DEVICE_FRONTEND:
2194                         deviceFileName = String.format(dvbDeviceFound
2195                                 ? "/dev/dvb/adapter%d/frontend%d" : "/dev/dvb%d.frontend%d",
2196                                 info.getAdapterId(), info.getDeviceId());
2197                         break;
2198                     default:
2199                         throw new IllegalArgumentException("Invalid DVB device: " + deviceType);
2200                 }
2201                 try {
2202                     // The DVB frontend device only needs to be opened in read/write mode, which
2203                     // allows performing tuning operations. The DVB demux and DVR device are enough
2204                     // to be opened in read only mode.
2205                     return ParcelFileDescriptor.open(new File(deviceFileName),
2206                             TvInputManager.DVB_DEVICE_FRONTEND == deviceType
2207                                     ? ParcelFileDescriptor.MODE_READ_WRITE
2208                                     : ParcelFileDescriptor.MODE_READ_ONLY);
2209                 } catch (FileNotFoundException e) {
2210                     return null;
2211                 }
2212             } finally {
2213                 Binder.restoreCallingIdentity(identity);
2214             }
2215         }
2216 
2217         @Override
getAvailableTvStreamConfigList(String inputId, int userId)2218         public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
2219                 throws RemoteException {
2220             ensureCaptureTvInputPermission();
2221 
2222             final long identity = Binder.clearCallingIdentity();
2223             final int callingUid = Binder.getCallingUid();
2224             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
2225                     userId, "getAvailableTvStreamConfigList");
2226             try {
2227                 return mTvInputHardwareManager.getAvailableTvStreamConfigList(
2228                         inputId, callingUid, resolvedUserId);
2229             } finally {
2230                 Binder.restoreCallingIdentity(identity);
2231             }
2232         }
2233 
2234         @Override
captureFrame(String inputId, Surface surface, TvStreamConfig config, int userId)2235         public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
2236                 int userId)
2237                 throws RemoteException {
2238             ensureCaptureTvInputPermission();
2239 
2240             final long identity = Binder.clearCallingIdentity();
2241             final int callingUid = Binder.getCallingUid();
2242             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
2243                     userId, "captureFrame");
2244             try {
2245                 String hardwareInputId = null;
2246                 synchronized (mLock) {
2247                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
2248                     if (userState.inputMap.get(inputId) == null) {
2249                         Slog.e(TAG, "input not found for " + inputId);
2250                         return false;
2251                     }
2252                     for (SessionState sessionState : userState.sessionStateMap.values()) {
2253                         if (sessionState.inputId.equals(inputId)
2254                                 && sessionState.hardwareSessionToken != null) {
2255                             hardwareInputId = userState.sessionStateMap.get(
2256                                     sessionState.hardwareSessionToken).inputId;
2257                             break;
2258                         }
2259                     }
2260                 }
2261                 return mTvInputHardwareManager.captureFrame(
2262                         (hardwareInputId != null) ? hardwareInputId : inputId,
2263                         surface, config, callingUid, resolvedUserId);
2264             } finally {
2265                 Binder.restoreCallingIdentity(identity);
2266             }
2267         }
2268 
2269         @Override
isSingleSessionActive(int userId)2270         public boolean isSingleSessionActive(int userId) throws RemoteException {
2271             ensureCaptureTvInputPermission();
2272             final long identity = Binder.clearCallingIdentity();
2273             final int callingUid = Binder.getCallingUid();
2274             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
2275                     userId, "isSingleSessionActive");
2276             try {
2277                 synchronized (mLock) {
2278                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
2279                     if (userState.sessionStateMap.size() == 1) {
2280                         return true;
2281                     } else if (userState.sessionStateMap.size() == 2) {
2282                         SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
2283                                 new SessionState[2]);
2284                         // Check if there is a wrapper input.
2285                         return sessionStates[0].hardwareSessionToken != null
2286                                 || sessionStates[1].hardwareSessionToken != null;
2287                     }
2288                     return false;
2289                 }
2290             } finally {
2291                 Binder.restoreCallingIdentity(identity);
2292             }
2293         }
2294 
ensureCaptureTvInputPermission()2295         private void ensureCaptureTvInputPermission() {
2296             if (mContext.checkCallingPermission(
2297                 android.Manifest.permission.CAPTURE_TV_INPUT)
2298                 != PackageManager.PERMISSION_GRANTED) {
2299                 throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
2300             }
2301         }
2302 
2303         @Override
requestChannelBrowsable(Uri channelUri, int userId)2304         public void requestChannelBrowsable(Uri channelUri, int userId)
2305                 throws RemoteException {
2306             final String callingPackageName = getCallingPackageName();
2307             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
2308                     Binder.getCallingUid(), userId, "requestChannelBrowsable");
2309             final long identity = Binder.clearCallingIdentity();
2310             try {
2311                 Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
2312                 List<ResolveInfo> list = getContext().getPackageManager()
2313                     .queryBroadcastReceivers(intent, 0);
2314                 if (list != null) {
2315                     for (ResolveInfo info : list) {
2316                         String receiverPackageName = info.activityInfo.packageName;
2317                         intent.putExtra(TvContract.EXTRA_CHANNEL_ID, ContentUris.parseId(
2318                                 channelUri));
2319                         intent.putExtra(TvContract.EXTRA_PACKAGE_NAME, callingPackageName);
2320                         intent.setPackage(receiverPackageName);
2321                         getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
2322                     }
2323                 }
2324             } finally {
2325                 Binder.restoreCallingIdentity(identity);
2326             }
2327         }
2328 
2329         @Override
getClientPid(String sessionId)2330         public int getClientPid(String sessionId) {
2331             ensureTunerResourceAccessPermission();
2332             final long identity = Binder.clearCallingIdentity();
2333 
2334             int clientPid = TvInputManager.UNKNOWN_CLIENT_PID;
2335             try {
2336                 synchronized (mLock) {
2337                     try {
2338                         clientPid = getClientPidLocked(sessionId);
2339                     } catch (ClientPidNotFoundException e) {
2340                         Slog.e(TAG, "error in getClientPid", e);
2341                     }
2342                 }
2343             } finally {
2344                 Binder.restoreCallingIdentity(identity);
2345             }
2346             return clientPid;
2347         }
2348 
2349         @Override
getCurrentTunedInfos(@serIdInt int userId)2350         public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) {
2351             if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO)
2352                     != PackageManager.PERMISSION_GRANTED) {
2353                 throw new SecurityException(
2354                         "The caller does not have access tuned info permission");
2355             }
2356             int callingPid = Binder.getCallingPid();
2357             int callingUid = Binder.getCallingUid();
2358             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
2359                     "getTvCurrentChannelInfos");
2360             synchronized (mLock) {
2361                 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
2362                 return getCurrentTunedInfosInternalLocked(userState, callingPid, callingUid);
2363             }
2364         }
2365 
2366         /**
2367          * Add a hardware device in the TvInputHardwareManager for CTS testing
2368          * purpose.
2369          *
2370          * @param deviceId  the id of the adding hardware device.
2371          */
2372         @Override
addHardwareDevice(int deviceId)2373         public void addHardwareDevice(int deviceId) {
2374             TvInputHardwareInfo info = new TvInputHardwareInfo.Builder()
2375                         .deviceId(deviceId)
2376                         .type(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI)
2377                         .audioType(DEVICE_NONE)
2378                         .audioAddress("0")
2379                         .hdmiPortId(0)
2380                         .build();
2381             mTvInputHardwareManager.onDeviceAvailable(info, null);
2382         }
2383 
2384         /**
2385          * Remove a hardware device in the TvInputHardwareManager for CTS testing
2386          * purpose.
2387          *
2388          * @param deviceId the id of the removing hardware device.
2389          */
2390         @Override
removeHardwareDevice(int deviceId)2391         public void removeHardwareDevice(int deviceId) {
2392             mTvInputHardwareManager.onDeviceUnavailable(deviceId);
2393         }
2394 
2395         @GuardedBy("mLock")
getClientPidLocked(String sessionId)2396         private int getClientPidLocked(String sessionId)
2397                 throws IllegalStateException {
2398             if (mSessionIdToSessionStateMap.get(sessionId) == null) {
2399                 throw new IllegalStateException("Client Pid not found with sessionId "
2400                         + sessionId);
2401             }
2402             return mSessionIdToSessionStateMap.get(sessionId).callingPid;
2403         }
2404 
ensureTunerResourceAccessPermission()2405         private void ensureTunerResourceAccessPermission() {
2406             if (mContext.checkCallingPermission(
2407                     android.Manifest.permission.TUNER_RESOURCE_ACCESS)
2408                     != PackageManager.PERMISSION_GRANTED) {
2409                 throw new SecurityException("Requires TUNER_RESOURCE_ACCESS permission");
2410             }
2411         }
2412 
2413         @Override
2414         @SuppressWarnings("resource")
dump(FileDescriptor fd, final PrintWriter writer, String[] args)2415         protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
2416             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
2417             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
2418 
2419             synchronized (mLock) {
2420                 pw.println("User Ids (Current user: " + mCurrentUserId + "):");
2421                 pw.increaseIndent();
2422                 for (int i = 0; i < mUserStates.size(); i++) {
2423                     int userId = mUserStates.keyAt(i);
2424                     pw.println(Integer.valueOf(userId));
2425                 }
2426                 pw.decreaseIndent();
2427 
2428                 for (int i = 0; i < mUserStates.size(); i++) {
2429                     int userId = mUserStates.keyAt(i);
2430                     UserState userState = getOrCreateUserStateLocked(userId);
2431                     pw.println("UserState (" + userId + "):");
2432                     pw.increaseIndent();
2433 
2434                     pw.println("inputMap: inputId -> TvInputState");
2435                     pw.increaseIndent();
2436                     for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) {
2437                         pw.println(entry.getKey() + ": " + entry.getValue());
2438                     }
2439                     pw.decreaseIndent();
2440 
2441                     pw.println("packageSet:");
2442                     pw.increaseIndent();
2443                     for (String packageName : userState.packageSet) {
2444                         pw.println(packageName);
2445                     }
2446                     pw.decreaseIndent();
2447 
2448                     pw.println("clientStateMap: ITvInputClient -> ClientState");
2449                     pw.increaseIndent();
2450                     for (Map.Entry<IBinder, ClientState> entry :
2451                             userState.clientStateMap.entrySet()) {
2452                         ClientState client = entry.getValue();
2453                         pw.println(entry.getKey() + ": " + client);
2454 
2455                         pw.increaseIndent();
2456 
2457                         pw.println("sessionTokens:");
2458                         pw.increaseIndent();
2459                         for (IBinder token : client.sessionTokens) {
2460                             pw.println("" + token);
2461                         }
2462                         pw.decreaseIndent();
2463 
2464                         pw.println("clientTokens: " + client.clientToken);
2465                         pw.println("userId: " + client.userId);
2466 
2467                         pw.decreaseIndent();
2468                     }
2469                     pw.decreaseIndent();
2470 
2471                     pw.println("serviceStateMap: ComponentName -> ServiceState");
2472                     pw.increaseIndent();
2473                     for (Map.Entry<ComponentName, ServiceState> entry :
2474                             userState.serviceStateMap.entrySet()) {
2475                         ServiceState service = entry.getValue();
2476                         pw.println(entry.getKey() + ": " + service);
2477 
2478                         pw.increaseIndent();
2479 
2480                         pw.println("sessionTokens:");
2481                         pw.increaseIndent();
2482                         for (IBinder token : service.sessionTokens) {
2483                             pw.println("" + token);
2484                         }
2485                         pw.decreaseIndent();
2486 
2487                         pw.println("service: " + service.service);
2488                         pw.println("callback: " + service.callback);
2489                         pw.println("bound: " + service.bound);
2490                         pw.println("reconnecting: " + service.reconnecting);
2491 
2492                         pw.decreaseIndent();
2493                     }
2494                     pw.decreaseIndent();
2495 
2496                     pw.println("sessionStateMap: ITvInputSession -> SessionState");
2497                     pw.increaseIndent();
2498                     for (Map.Entry<IBinder, SessionState> entry :
2499                             userState.sessionStateMap.entrySet()) {
2500                         SessionState session = entry.getValue();
2501                         pw.println(entry.getKey() + ": " + session);
2502 
2503                         pw.increaseIndent();
2504                         pw.println("inputId: " + session.inputId);
2505                         pw.println("sessionId: " + session.sessionId);
2506                         pw.println("client: " + session.client);
2507                         pw.println("seq: " + session.seq);
2508                         pw.println("callingUid: " + session.callingUid);
2509                         pw.println("callingPid: " + session.callingPid);
2510                         pw.println("userId: " + session.userId);
2511                         pw.println("sessionToken: " + session.sessionToken);
2512                         pw.println("session: " + session.session);
2513                         pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
2514                         pw.decreaseIndent();
2515                     }
2516                     pw.decreaseIndent();
2517 
2518                     pw.println("mCallbacks:");
2519                     pw.increaseIndent();
2520                     int n = userState.mCallbacks.beginBroadcast();
2521                     for (int j = 0; j < n; ++j) {
2522                         pw.println(userState.mCallbacks.getRegisteredCallbackItem(j));
2523                     }
2524                     userState.mCallbacks.finishBroadcast();
2525                     pw.decreaseIndent();
2526 
2527                     pw.println("mainSessionToken: " + userState.mainSessionToken);
2528                     pw.decreaseIndent();
2529                 }
2530             }
2531             mTvInputHardwareManager.dump(fd, writer, args);
2532         }
2533     }
2534 
2535     @Nullable
getTvInputState( SessionState sessionState, @Nullable UserState userState)2536     private static TvInputState getTvInputState(
2537             SessionState sessionState,
2538             @Nullable UserState userState) {
2539         if (userState != null) {
2540             return userState.inputMap.get(sessionState.inputId);
2541         }
2542         return null;
2543     }
2544 
2545     @GuardedBy("mLock")
getCurrentTunedInfosInternalLocked( UserState userState, int callingPid, int callingUid)2546     private List<TunedInfo> getCurrentTunedInfosInternalLocked(
2547             UserState userState, int callingPid, int callingUid) {
2548         List<TunedInfo> channelInfos = new ArrayList<>();
2549         boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid);
2550         for (SessionState state : userState.sessionStateMap.values()) {
2551             if (state.isCurrent) {
2552                 Integer appTag;
2553                 int appType;
2554                 if (state.callingUid == callingUid) {
2555                     appTag = APP_TAG_SELF;
2556                     appType = TunedInfo.APP_TYPE_SELF;
2557                 } else {
2558                     appTag = userState.mAppTagMap.get(state.callingUid);
2559                     if (appTag == null) {
2560                         appTag = userState.mNextAppTag++;
2561                         userState.mAppTagMap.put(state.callingUid, appTag);
2562                     }
2563                     appType = isSystemApp(state.componentName.getPackageName())
2564                             ? TunedInfo.APP_TYPE_SYSTEM
2565                             : TunedInfo.APP_TYPE_NON_SYSTEM;
2566                 }
2567                 channelInfos.add(new TunedInfo(
2568                         state.inputId,
2569                         watchedProgramsAccess ? state.currentChannel : null,
2570                         state.isRecordingSession,
2571                         state.isVisible,
2572                         state.isMainSession,
2573                         appType,
2574                         appTag));
2575             }
2576         }
2577         return channelInfos;
2578     }
2579 
hasAccessWatchedProgramsPermission(int callingPid, int callingUid)2580     private boolean hasAccessWatchedProgramsPermission(int callingPid, int callingUid) {
2581         return mContext.checkPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS, callingPid, callingUid)
2582                 == PackageManager.PERMISSION_GRANTED;
2583     }
2584 
isSystemApp(String pkg)2585     private boolean isSystemApp(String pkg) {
2586         try {
2587             return (mContext.getPackageManager().getApplicationInfo(pkg, 0).flags
2588                     & ApplicationInfo.FLAG_SYSTEM) != 0;
2589         } catch (NameNotFoundException e) {
2590             return false;
2591         }
2592     }
2593 
2594     /**
2595      * Log Tune state changes to {@link FrameworkStatsLog}.
2596      *
2597      * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
2598      * Inspect the code at:
2599      *
2600      * <ul>
2601      *   <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
2602      *   <li>{@link #logTuneStateChanged}
2603      *   <li>{@link TvInputManagerService.BinderService#createSession}
2604      *   <li>{@link SessionState#sessionId}
2605      * </ul>
2606      */
logTuneStateChanged(int state, SessionState sessionState, @Nullable TvInputState inputState)2607     private void logTuneStateChanged(int state, SessionState sessionState,
2608             @Nullable TvInputState inputState) {
2609         int tisUid = Process.INVALID_UID;
2610         int inputType = FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__TYPE__TIF_INPUT_TYPE_UNKNOWN;
2611         int inputId = 0;
2612         int hdmiPort = 0;
2613         if (inputState != null) {
2614             tisUid = inputState.uid;
2615             inputType = inputState.info.getType();
2616             if (inputType == TvInputInfo.TYPE_TUNER) {
2617                 inputType = FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__TYPE__TUNER;
2618             }
2619             inputId = inputState.inputNumber;
2620             HdmiDeviceInfo hdmiDeviceInfo = inputState.info.getHdmiDeviceInfo();
2621             if (hdmiDeviceInfo != null) {
2622                 hdmiPort = hdmiDeviceInfo.getPortId();
2623             }
2624         }
2625         FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED,
2626                 new int[]{sessionState.callingUid,
2627                         tisUid},
2628                 new String[]{"tif_player", "tv_input_service"},
2629                 state,
2630                 sessionState.sessionId,
2631                 inputType,
2632                 inputId,
2633                 hdmiPort);
2634     }
2635 
2636     private static final class UserState {
2637         // A mapping from the TV input id to its TvInputState.
2638         private Map<String, TvInputState> inputMap = new HashMap<>();
2639 
2640         // A set of all TV input packages.
2641         private final Set<String> packageSet = new HashSet<>();
2642 
2643         // A list of all TV content rating systems defined.
2644         private final List<TvContentRatingSystemInfo>
2645                 contentRatingSystemList = new ArrayList<>();
2646 
2647         // A mapping from the token of a client to its state.
2648         private final Map<IBinder, ClientState> clientStateMap = new HashMap<>();
2649 
2650         // A mapping from the name of a TV input service to its state.
2651         private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>();
2652 
2653         // A mapping from the token of a TV input session to its state.
2654         private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>();
2655 
2656         // A list of callbacks.
2657         private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks =
2658                 new RemoteCallbackList<>();
2659 
2660         private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap =
2661                 new HashMap<>();
2662 
2663         // The token of a "main" TV input session.
2664         private IBinder mainSessionToken = null;
2665 
2666         // Persistent data store for all internal settings maintained by the TV input manager
2667         // service.
2668         private final PersistentDataStore persistentDataStore;
2669 
2670         @GuardedBy("TvInputManagerService.this.mLock")
2671         private final Map<Integer, Integer> mAppTagMap = new HashMap<>();
2672         @GuardedBy("TvInputManagerService.this.mLock")
2673         private int mNextAppTag = 1;
2674 
UserState(Context context, int userId)2675         private UserState(Context context, int userId) {
2676             persistentDataStore = new PersistentDataStore(context, userId);
2677         }
2678     }
2679 
2680     private final class ClientState implements IBinder.DeathRecipient {
2681         private final List<IBinder> sessionTokens = new ArrayList<>();
2682 
2683         private IBinder clientToken;
2684         private final int userId;
2685 
ClientState(IBinder clientToken, int userId)2686         ClientState(IBinder clientToken, int userId) {
2687             this.clientToken = clientToken;
2688             this.userId = userId;
2689         }
2690 
isEmpty()2691         public boolean isEmpty() {
2692             return sessionTokens.isEmpty();
2693         }
2694 
2695         @Override
binderDied()2696         public void binderDied() {
2697             synchronized (mLock) {
2698                 UserState userState = getOrCreateUserStateLocked(userId);
2699                 // DO NOT remove the client state of clientStateMap in this method. It will be
2700                 // removed in releaseSessionLocked().
2701                 ClientState clientState = userState.clientStateMap.get(clientToken);
2702                 if (clientState != null) {
2703                     while (clientState.sessionTokens.size() > 0) {
2704                         IBinder sessionToken = clientState.sessionTokens.get(0);
2705                         releaseSessionLocked(
2706                                 sessionToken, Process.SYSTEM_UID, userId);
2707                         // the releaseSessionLocked function may return before the sessionToken
2708                         // is removed if the related sessionState is null. So need to check again
2709                         // to avoid death curculation.
2710                         if (clientState.sessionTokens.contains(sessionToken)) {
2711                             Slog.d(TAG, "remove sessionToken " + sessionToken + " for " + clientToken);
2712                             clientState.sessionTokens.remove(sessionToken);
2713                         }
2714                     }
2715                 }
2716                 clientToken = null;
2717             }
2718         }
2719     }
2720 
2721     private final class ServiceState {
2722         private final List<IBinder> sessionTokens = new ArrayList<>();
2723         private final ServiceConnection connection;
2724         private final ComponentName component;
2725         private final boolean isHardware;
2726         private final Map<String, TvInputInfo> hardwareInputMap = new HashMap<>();
2727 
2728         private ITvInputService service;
2729         private ServiceCallback callback;
2730         private boolean bound;
2731         private boolean reconnecting;
2732 
ServiceState(ComponentName component, int userId)2733         private ServiceState(ComponentName component, int userId) {
2734             this.component = component;
2735             this.connection = new InputServiceConnection(component, userId);
2736             this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
2737         }
2738     }
2739 
2740     private static final class TvInputState {
2741 
2742         /** A TvInputInfo object which represents the TV input. */
2743         private TvInputInfo info;
2744 
2745         /**
2746          * ID unique to a specific TvInputService.
2747          */
2748         private int inputNumber;
2749 
2750         /**
2751          * The kernel user-ID that has been assigned to the application the TvInput is a part of.
2752          *
2753          * <p>
2754          * Currently this is not a unique ID (multiple applications can have
2755          * the same uid).
2756          */
2757         private int uid;
2758 
2759         /**
2760          * The state of TV input.
2761          *
2762          * <p>
2763          * Connected by default
2764          */
2765         private int state = INPUT_STATE_CONNECTED;
2766 
2767         @Override
toString()2768         public String toString() {
2769             return "info: " + info + "; state: " + state;
2770         }
2771     }
2772 
2773     private final class SessionState implements IBinder.DeathRecipient {
2774         private final String inputId;
2775 
2776         /**
2777          * A randomly generated id for this this session.
2778          *
2779          * <p>This field contains no user or device reference and is large enough to be
2780          * effectively globally unique.
2781          *
2782          * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
2783          * Inspect the code at:
2784          *
2785          * <ul>
2786          *   <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
2787          *   <li>{@link #logTuneStateChanged}
2788          *   <li>{@link TvInputManagerService.BinderService#createSession}
2789          *   <li>{@link SessionState#sessionId}
2790          * </ul>
2791          */
2792         private final String sessionId;
2793         private final ComponentName componentName;
2794         private final boolean isRecordingSession;
2795         private final ITvInputClient client;
2796         private final int seq;
2797         /**
2798          * The {code UID} of the application that created the session.
2799          *
2800          * <p>
2801          * The application is usually the TIF Player.
2802          */
2803         private final int callingUid;
2804         /**
2805          * The  {@code PID} of the application that created the session.
2806          *
2807          * <p>
2808          * The application is usually the TIF Player.
2809          */
2810         private final int callingPid;
2811         private final int userId;
2812         private final IBinder sessionToken;
2813         private ITvInputSession session;
2814         // Not null if this session represents an external device connected to a hardware TV input.
2815         private IBinder hardwareSessionToken;
2816 
2817         private boolean isCurrent = false;
2818         private Uri currentChannel = null;
2819         private boolean isVisible = false;
2820         private boolean isMainSession = false;
2821 
SessionState(IBinder sessionToken, String inputId, ComponentName componentName, boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, int callingPid, int userId, String sessionId)2822         private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
2823                 boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
2824                 int callingPid, int userId, String sessionId) {
2825             this.sessionToken = sessionToken;
2826             this.inputId = inputId;
2827             this.componentName = componentName;
2828             this.isRecordingSession = isRecordingSession;
2829             this.client = client;
2830             this.seq = seq;
2831             this.callingUid = callingUid;
2832             this.callingPid = callingPid;
2833             this.userId = userId;
2834             this.sessionId = sessionId;
2835         }
2836 
2837         @Override
binderDied()2838         public void binderDied() {
2839             synchronized (mLock) {
2840                 session = null;
2841                 clearSessionAndNotifyClientLocked(this);
2842             }
2843         }
2844     }
2845 
2846     private final class InputServiceConnection implements ServiceConnection {
2847         private final ComponentName mComponent;
2848         private final int mUserId;
2849 
InputServiceConnection(ComponentName component, int userId)2850         private InputServiceConnection(ComponentName component, int userId) {
2851             mComponent = component;
2852             mUserId = userId;
2853         }
2854 
2855         @Override
onServiceConnected(ComponentName component, IBinder service)2856         public void onServiceConnected(ComponentName component, IBinder service) {
2857             if (DEBUG) {
2858                 Slog.d(TAG, "onServiceConnected(component=" + component + ")");
2859             }
2860             synchronized (mLock) {
2861                 UserState userState = getUserStateLocked(mUserId);
2862                 if (userState == null) {
2863                     // The user was removed while connecting.
2864                     mContext.unbindService(this);
2865                     return;
2866                 }
2867                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
2868                 serviceState.service = ITvInputService.Stub.asInterface(service);
2869 
2870                 // Register a callback, if we need to.
2871                 if (serviceState.isHardware && serviceState.callback == null) {
2872                     serviceState.callback = new ServiceCallback(mComponent, mUserId);
2873                     try {
2874                         serviceState.service.registerCallback(serviceState.callback);
2875                     } catch (RemoteException e) {
2876                         Slog.e(TAG, "error in registerCallback", e);
2877                     }
2878                 }
2879 
2880                 List<IBinder> tokensToBeRemoved = new ArrayList<>();
2881 
2882                 // And create sessions, if any.
2883                 for (IBinder sessionToken : serviceState.sessionTokens) {
2884                     if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
2885                         tokensToBeRemoved.add(sessionToken);
2886                     }
2887                 }
2888 
2889                 for (IBinder sessionToken : tokensToBeRemoved) {
2890                     removeSessionStateLocked(sessionToken, mUserId);
2891                 }
2892 
2893                 for (TvInputState inputState : userState.inputMap.values()) {
2894                     if (inputState.info.getComponent().equals(component)
2895                             && inputState.state != INPUT_STATE_CONNECTED) {
2896                         notifyInputStateChangedLocked(userState, inputState.info.getId(),
2897                                 inputState.state, null);
2898                     }
2899                 }
2900 
2901                 if (serviceState.isHardware) {
2902                     serviceState.hardwareInputMap.clear();
2903                     for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) {
2904                         try {
2905                             serviceState.service.notifyHardwareAdded(hardware);
2906                         } catch (RemoteException e) {
2907                             Slog.e(TAG, "error in notifyHardwareAdded", e);
2908                         }
2909                     }
2910                     for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
2911                         try {
2912                             serviceState.service.notifyHdmiDeviceAdded(device);
2913                         } catch (RemoteException e) {
2914                             Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
2915                         }
2916                     }
2917                 }
2918             }
2919         }
2920 
2921         @Override
onServiceDisconnected(ComponentName component)2922         public void onServiceDisconnected(ComponentName component) {
2923             if (DEBUG) {
2924                 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")");
2925             }
2926             if (!mComponent.equals(component)) {
2927                 throw new IllegalArgumentException("Mismatched ComponentName: "
2928                         + mComponent + " (expected), " + component + " (actual).");
2929             }
2930             synchronized (mLock) {
2931                 UserState userState = getOrCreateUserStateLocked(mUserId);
2932                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
2933                 if (serviceState != null) {
2934                     serviceState.reconnecting = true;
2935                     serviceState.bound = false;
2936                     serviceState.service = null;
2937                     serviceState.callback = null;
2938 
2939                     abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
2940                 }
2941             }
2942         }
2943     }
2944 
2945     private final class ServiceCallback extends ITvInputServiceCallback.Stub {
2946         private final ComponentName mComponent;
2947         private final int mUserId;
2948 
ServiceCallback(ComponentName component, int userId)2949         ServiceCallback(ComponentName component, int userId) {
2950             mComponent = component;
2951             mUserId = userId;
2952         }
2953 
ensureHardwarePermission()2954         private void ensureHardwarePermission() {
2955             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2956                     != PackageManager.PERMISSION_GRANTED) {
2957                 throw new SecurityException("The caller does not have hardware permission");
2958             }
2959         }
2960 
ensureValidInput(TvInputInfo inputInfo)2961         private void ensureValidInput(TvInputInfo inputInfo) {
2962             if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) {
2963                 throw new IllegalArgumentException("Invalid TvInputInfo");
2964             }
2965         }
2966 
2967         @GuardedBy("mLock")
addHardwareInputLocked(TvInputInfo inputInfo)2968         private void addHardwareInputLocked(TvInputInfo inputInfo) {
2969             ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
2970             serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
2971             buildTvInputListLocked(mUserId, null);
2972         }
2973 
addHardwareInput(int deviceId, TvInputInfo inputInfo)2974         public void addHardwareInput(int deviceId, TvInputInfo inputInfo) {
2975             ensureHardwarePermission();
2976             ensureValidInput(inputInfo);
2977             synchronized (mLock) {
2978                 mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
2979                 addHardwareInputLocked(inputInfo);
2980             }
2981         }
2982 
addHdmiInput(int id, TvInputInfo inputInfo)2983         public void addHdmiInput(int id, TvInputInfo inputInfo) {
2984             ensureHardwarePermission();
2985             ensureValidInput(inputInfo);
2986             synchronized (mLock) {
2987                 mTvInputHardwareManager.addHdmiInput(id, inputInfo);
2988                 addHardwareInputLocked(inputInfo);
2989             }
2990         }
2991 
removeHardwareInput(String inputId)2992         public void removeHardwareInput(String inputId) {
2993             ensureHardwarePermission();
2994             synchronized (mLock) {
2995                 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
2996                 boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
2997                 if (removed) {
2998                     buildTvInputListLocked(mUserId, null);
2999                     mTvInputHardwareManager.removeHardwareInput(inputId);
3000                 } else {
3001                     Slog.e(TAG, "failed to remove input " + inputId);
3002                 }
3003             }
3004         }
3005     }
3006 
3007     private final class SessionCallback extends ITvInputSessionCallback.Stub {
3008         private final SessionState mSessionState;
3009         private final InputChannel[] mChannels;
3010 
SessionCallback(SessionState sessionState, InputChannel[] channels)3011         SessionCallback(SessionState sessionState, InputChannel[] channels) {
3012             mSessionState = sessionState;
3013             mChannels = channels;
3014         }
3015 
3016         @Override
onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken)3017         public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) {
3018             if (DEBUG) {
3019                 Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")");
3020             }
3021             synchronized (mLock) {
3022                 mSessionState.session = session;
3023                 mSessionState.hardwareSessionToken = hardwareSessionToken;
3024                 if (session != null && addSessionTokenToClientStateLocked(session)) {
3025                     sendSessionTokenToClientLocked(mSessionState.client,
3026                             mSessionState.inputId, mSessionState.sessionToken, mChannels[0],
3027                             mSessionState.seq);
3028                 } else {
3029                     removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId);
3030                     sendSessionTokenToClientLocked(mSessionState.client,
3031                             mSessionState.inputId, null, null, mSessionState.seq);
3032                 }
3033                 mChannels[0].dispose();
3034             }
3035         }
3036 
3037         @GuardedBy("mLock")
addSessionTokenToClientStateLocked(ITvInputSession session)3038         private boolean addSessionTokenToClientStateLocked(ITvInputSession session) {
3039             try {
3040                 session.asBinder().linkToDeath(mSessionState, 0);
3041             } catch (RemoteException e) {
3042                 Slog.e(TAG, "session process has already died", e);
3043                 return false;
3044             }
3045 
3046             IBinder clientToken = mSessionState.client.asBinder();
3047             UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
3048             ClientState clientState = userState.clientStateMap.get(clientToken);
3049             if (clientState == null) {
3050                 clientState = new ClientState(clientToken, mSessionState.userId);
3051                 try {
3052                     clientToken.linkToDeath(clientState, 0);
3053                 } catch (RemoteException e) {
3054                     Slog.e(TAG, "client process has already died", e);
3055                     return false;
3056                 }
3057                 userState.clientStateMap.put(clientToken, clientState);
3058             }
3059             clientState.sessionTokens.add(mSessionState.sessionToken);
3060             return true;
3061         }
3062 
3063         @Override
onChannelRetuned(Uri channelUri)3064         public void onChannelRetuned(Uri channelUri) {
3065             synchronized (mLock) {
3066                 if (DEBUG) {
3067                     Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
3068                 }
3069                 if (mSessionState.session == null || mSessionState.client == null) {
3070                     return;
3071                 }
3072                 try {
3073                     // TODO: Consider adding this channel change in the watch log. When we do
3074                     // that, how we can protect the watch log from malicious tv inputs should
3075                     // be addressed. e.g. add a field which represents where the channel change
3076                     // originated from.
3077                     mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq);
3078                     if (!mSessionState.isCurrent
3079                             || !Objects.equals(mSessionState.currentChannel, channelUri)) {
3080                         UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
3081                         mSessionState.isCurrent = true;
3082                         mSessionState.currentChannel = channelUri;
3083                         notifyCurrentChannelInfosUpdatedLocked(userState);
3084                     }
3085                 } catch (RemoteException e) {
3086                     Slog.e(TAG, "error in onChannelRetuned", e);
3087                 }
3088             }
3089         }
3090 
3091         @Override
onTracksChanged(List<TvTrackInfo> tracks)3092         public void onTracksChanged(List<TvTrackInfo> tracks) {
3093             synchronized (mLock) {
3094                 if (DEBUG) {
3095                     Slog.d(TAG, "onTracksChanged(" + tracks + ")");
3096                 }
3097                 if (mSessionState.session == null || mSessionState.client == null) {
3098                     return;
3099                 }
3100                 try {
3101                     mSessionState.client.onTracksChanged(tracks, mSessionState.seq);
3102                 } catch (RemoteException e) {
3103                     Slog.e(TAG, "error in onTracksChanged", e);
3104                 }
3105             }
3106         }
3107 
3108         @Override
onTrackSelected(int type, String trackId)3109         public void onTrackSelected(int type, String trackId) {
3110             synchronized (mLock) {
3111                 if (DEBUG) {
3112                     Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
3113                 }
3114                 if (mSessionState.session == null || mSessionState.client == null) {
3115                     return;
3116                 }
3117                 try {
3118                     mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq);
3119                 } catch (RemoteException e) {
3120                     Slog.e(TAG, "error in onTrackSelected", e);
3121                 }
3122             }
3123         }
3124 
3125         @Override
onVideoAvailable()3126         public void onVideoAvailable() {
3127             synchronized (mLock) {
3128                 if (DEBUG) {
3129                     Slog.d(TAG, "onVideoAvailable()");
3130                 }
3131                 if (mSessionState.session == null || mSessionState.client == null) {
3132                     return;
3133                 }
3134                 TvInputState tvInputState = getTvInputState(mSessionState,
3135                         getUserStateLocked(mCurrentUserId));
3136                 try {
3137                     mSessionState.client.onVideoAvailable(mSessionState.seq);
3138                     logTuneStateChanged(
3139                             FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE,
3140                             mSessionState, tvInputState);
3141                 } catch (RemoteException e) {
3142                     Slog.e(TAG, "error in onVideoAvailable", e);
3143                 }
3144             }
3145         }
3146 
3147         @Override
onVideoUnavailable(int reason)3148         public void onVideoUnavailable(int reason) {
3149             synchronized (mLock) {
3150                 if (DEBUG) {
3151                     Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
3152                 }
3153                 if (mSessionState.session == null || mSessionState.client == null) {
3154                     return;
3155                 }
3156                 TvInputState tvInputState = getTvInputState(mSessionState,
3157                         getUserStateLocked(mCurrentUserId));
3158                 try {
3159                     mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
3160                     int loggedReason = getVideoUnavailableReasonForStatsd(reason);
3161                     logTuneStateChanged(loggedReason, mSessionState, tvInputState);
3162                 } catch (RemoteException e) {
3163                     Slog.e(TAG, "error in onVideoUnavailable", e);
3164                 }
3165             }
3166         }
3167 
3168         @Override
onContentAllowed()3169         public void onContentAllowed() {
3170             synchronized (mLock) {
3171                 if (DEBUG) {
3172                     Slog.d(TAG, "onContentAllowed()");
3173                 }
3174                 if (mSessionState.session == null || mSessionState.client == null) {
3175                     return;
3176                 }
3177                 try {
3178                     mSessionState.client.onContentAllowed(mSessionState.seq);
3179                 } catch (RemoteException e) {
3180                     Slog.e(TAG, "error in onContentAllowed", e);
3181                 }
3182             }
3183         }
3184 
3185         @Override
onContentBlocked(String rating)3186         public void onContentBlocked(String rating) {
3187             synchronized (mLock) {
3188                 if (DEBUG) {
3189                     Slog.d(TAG, "onContentBlocked()");
3190                 }
3191                 if (mSessionState.session == null || mSessionState.client == null) {
3192                     return;
3193                 }
3194                 try {
3195                     mSessionState.client.onContentBlocked(rating, mSessionState.seq);
3196                 } catch (RemoteException e) {
3197                     Slog.e(TAG, "error in onContentBlocked", e);
3198                 }
3199             }
3200         }
3201 
3202         @Override
onLayoutSurface(int left, int top, int right, int bottom)3203         public void onLayoutSurface(int left, int top, int right, int bottom) {
3204             synchronized (mLock) {
3205                 if (DEBUG) {
3206                     Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
3207                             + ", right=" + right + ", bottom=" + bottom + ",)");
3208                 }
3209                 if (mSessionState.session == null || mSessionState.client == null) {
3210                     return;
3211                 }
3212                 try {
3213                     mSessionState.client.onLayoutSurface(left, top, right, bottom,
3214                             mSessionState.seq);
3215                 } catch (RemoteException e) {
3216                     Slog.e(TAG, "error in onLayoutSurface", e);
3217                 }
3218             }
3219         }
3220 
3221         @Override
onSessionEvent(String eventType, Bundle eventArgs)3222         public void onSessionEvent(String eventType, Bundle eventArgs) {
3223             synchronized (mLock) {
3224                 if (DEBUG) {
3225                     Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs
3226                             + ")");
3227                 }
3228                 if (mSessionState.session == null || mSessionState.client == null) {
3229                     return;
3230                 }
3231                 try {
3232                     mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq);
3233                 } catch (RemoteException e) {
3234                     Slog.e(TAG, "error in onSessionEvent", e);
3235                 }
3236             }
3237         }
3238 
3239         @Override
onTimeShiftStatusChanged(int status)3240         public void onTimeShiftStatusChanged(int status) {
3241             synchronized (mLock) {
3242                 if (DEBUG) {
3243                     Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")");
3244                 }
3245                 if (mSessionState.session == null || mSessionState.client == null) {
3246                     return;
3247                 }
3248                 try {
3249                     mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq);
3250                 } catch (RemoteException e) {
3251                     Slog.e(TAG, "error in onTimeShiftStatusChanged", e);
3252                 }
3253             }
3254         }
3255 
3256         @Override
onTimeShiftStartPositionChanged(long timeMs)3257         public void onTimeShiftStartPositionChanged(long timeMs) {
3258             synchronized (mLock) {
3259                 if (DEBUG) {
3260                     Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")");
3261                 }
3262                 if (mSessionState.session == null || mSessionState.client == null) {
3263                     return;
3264                 }
3265                 try {
3266                     mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq);
3267                 } catch (RemoteException e) {
3268                     Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e);
3269                 }
3270             }
3271         }
3272 
3273         @Override
onTimeShiftCurrentPositionChanged(long timeMs)3274         public void onTimeShiftCurrentPositionChanged(long timeMs) {
3275             synchronized (mLock) {
3276                 if (DEBUG) {
3277                     Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")");
3278                 }
3279                 if (mSessionState.session == null || mSessionState.client == null) {
3280                     return;
3281                 }
3282                 try {
3283                     mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs,
3284                             mSessionState.seq);
3285                 } catch (RemoteException e) {
3286                     Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e);
3287                 }
3288             }
3289         }
3290 
3291         // For the recording session only
3292         @Override
onTuned(Uri channelUri)3293         public void onTuned(Uri channelUri) {
3294             synchronized (mLock) {
3295                 if (DEBUG) {
3296                     Slog.d(TAG, "onTuned()");
3297                 }
3298                 if (mSessionState.session == null || mSessionState.client == null) {
3299                     return;
3300                 }
3301                 try {
3302                     mSessionState.client.onTuned(mSessionState.seq, channelUri);
3303                 } catch (RemoteException e) {
3304                     Slog.e(TAG, "error in onTuned", e);
3305                 }
3306             }
3307         }
3308 
3309         // For the recording session only
3310         @Override
onRecordingStopped(Uri recordedProgramUri)3311         public void onRecordingStopped(Uri recordedProgramUri) {
3312             synchronized (mLock) {
3313                 if (DEBUG) {
3314                     Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri
3315                             + ")");
3316                 }
3317                 if (mSessionState.session == null || mSessionState.client == null) {
3318                     return;
3319                 }
3320                 try {
3321                     mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq);
3322                 } catch (RemoteException e) {
3323                     Slog.e(TAG, "error in onRecordingStopped", e);
3324                 }
3325             }
3326         }
3327 
3328         // For the recording session only
3329         @Override
onError(int error)3330         public void onError(int error) {
3331             synchronized (mLock) {
3332                 if (DEBUG) {
3333                     Slog.d(TAG, "onError(error=" + error + ")");
3334                 }
3335                 if (mSessionState.session == null || mSessionState.client == null) {
3336                     return;
3337                 }
3338                 try {
3339                     mSessionState.client.onError(error, mSessionState.seq);
3340                 } catch (RemoteException e) {
3341                     Slog.e(TAG, "error in onError", e);
3342                 }
3343             }
3344         }
3345     }
3346 
3347     @VisibleForTesting
getVideoUnavailableReasonForStatsd( @vInputManager.VideoUnavailableReason int reason)3348     static int getVideoUnavailableReasonForStatsd(
3349             @TvInputManager.VideoUnavailableReason int reason) {
3350         int loggedReason = reason + FrameworkStatsLog
3351                 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
3352         if (loggedReason < FrameworkStatsLog
3353                 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN
3354                 || loggedReason > FrameworkStatsLog
3355                 .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) {
3356             loggedReason = FrameworkStatsLog
3357                     .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
3358         }
3359         return loggedReason;
3360     }
3361 
getUserStateLocked(int userId)3362     private UserState getUserStateLocked(int userId) {
3363         return mUserStates.get(userId);
3364     }
3365 
3366     private static final class WatchLogHandler extends Handler {
3367         // There are only two kinds of watch events that can happen on the system:
3368         // 1. The current TV input session is tuned to a new channel.
3369         // 2. The session is released for some reason.
3370         // The former indicates the end of the previous log entry, if any, followed by the start of
3371         // a new entry. The latter indicates the end of the most recent entry for the given session.
3372         // Here the system supplies the database the smallest set of information only that is
3373         // sufficient to consolidate the log entries while minimizing database operations in the
3374         // system service.
3375         static final int MSG_LOG_WATCH_START = 1;
3376         static final int MSG_LOG_WATCH_END = 2;
3377         static final int MSG_SWITCH_CONTENT_RESOLVER = 3;
3378 
3379         private ContentResolver mContentResolver;
3380 
WatchLogHandler(ContentResolver contentResolver, Looper looper)3381         WatchLogHandler(ContentResolver contentResolver, Looper looper) {
3382             super(looper);
3383             mContentResolver = contentResolver;
3384         }
3385 
3386         @Override
handleMessage(Message msg)3387         public void handleMessage(Message msg) {
3388             switch (msg.what) {
3389                 case MSG_LOG_WATCH_START: {
3390                     SomeArgs args = (SomeArgs) msg.obj;
3391                     String packageName = (String) args.arg1;
3392                     long watchStartTime = (long) args.arg2;
3393                     long channelId = (long) args.arg3;
3394                     Bundle tuneParams = (Bundle) args.arg4;
3395                     IBinder sessionToken = (IBinder) args.arg5;
3396 
3397                     ContentValues values = new ContentValues();
3398                     values.put(TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME, packageName);
3399                     values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
3400                             watchStartTime);
3401                     values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
3402                     if (tuneParams != null) {
3403                         values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS,
3404                                 encodeTuneParams(tuneParams));
3405                     }
3406                     values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
3407                             sessionToken.toString());
3408 
3409                     try{
3410                         mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
3411                     }catch(IllegalArgumentException ex){
3412                         Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_START", ex);
3413                     }
3414                     args.recycle();
3415                     break;
3416                 }
3417                 case MSG_LOG_WATCH_END: {
3418                     SomeArgs args = (SomeArgs) msg.obj;
3419                     IBinder sessionToken = (IBinder) args.arg1;
3420                     long watchEndTime = (long) args.arg2;
3421 
3422                     ContentValues values = new ContentValues();
3423                     values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
3424                             watchEndTime);
3425                     values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
3426                             sessionToken.toString());
3427 
3428                     try{
3429                         mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
3430                     }catch(IllegalArgumentException ex){
3431                         Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_END", ex);
3432                     }
3433                     args.recycle();
3434                     break;
3435                 }
3436                 case MSG_SWITCH_CONTENT_RESOLVER: {
3437                     mContentResolver = (ContentResolver) msg.obj;
3438                     break;
3439                 }
3440                 default: {
3441                     Slog.w(TAG, "unhandled message code: " + msg.what);
3442                     break;
3443                 }
3444             }
3445         }
3446 
encodeTuneParams(Bundle tuneParams)3447         private String encodeTuneParams(Bundle tuneParams) {
3448             StringBuilder builder = new StringBuilder();
3449             Set<String> keySet = tuneParams.keySet();
3450             Iterator<String> it = keySet.iterator();
3451             while (it.hasNext()) {
3452                 String key = it.next();
3453                 Object value = tuneParams.get(key);
3454                 if (value == null) {
3455                     continue;
3456                 }
3457                 builder.append(replaceEscapeCharacters(key));
3458                 builder.append("=");
3459                 builder.append(replaceEscapeCharacters(value.toString()));
3460                 if (it.hasNext()) {
3461                     builder.append(", ");
3462                 }
3463             }
3464             return builder.toString();
3465         }
3466 
replaceEscapeCharacters(String src)3467         private String replaceEscapeCharacters(String src) {
3468             final char ESCAPE_CHARACTER = '%';
3469             final String ENCODING_TARGET_CHARACTERS = "%=,";
3470             StringBuilder builder = new StringBuilder();
3471             for (char ch : src.toCharArray()) {
3472                 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) {
3473                     builder.append(ESCAPE_CHARACTER);
3474                 }
3475                 builder.append(ch);
3476             }
3477             return builder.toString();
3478         }
3479     }
3480 
3481     private final class HardwareListener implements TvInputHardwareManager.Listener {
3482         @Override
onStateChanged(String inputId, int state)3483         public void onStateChanged(String inputId, int state) {
3484             synchronized (mLock) {
3485                 setStateLocked(inputId, state, mCurrentUserId);
3486             }
3487         }
3488 
3489         @Override
onHardwareDeviceAdded(TvInputHardwareInfo info)3490         public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
3491             synchronized (mLock) {
3492                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
3493                 // Broadcast the event to all hardware inputs.
3494                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
3495                     if (!serviceState.isHardware || serviceState.service == null) continue;
3496                     try {
3497                         serviceState.service.notifyHardwareAdded(info);
3498                     } catch (RemoteException e) {
3499                         Slog.e(TAG, "error in notifyHardwareAdded", e);
3500                     }
3501                 }
3502             }
3503         }
3504 
3505         @Override
onHardwareDeviceRemoved(TvInputHardwareInfo info)3506         public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
3507             synchronized (mLock) {
3508                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
3509                 // Broadcast the event to all hardware inputs.
3510                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
3511                     if (!serviceState.isHardware || serviceState.service == null) continue;
3512                     try {
3513                         serviceState.service.notifyHardwareRemoved(info);
3514                     } catch (RemoteException e) {
3515                         Slog.e(TAG, "error in notifyHardwareRemoved", e);
3516                     }
3517                 }
3518             }
3519         }
3520 
3521         @Override
onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo)3522         public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
3523             synchronized (mLock) {
3524                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
3525                 // Broadcast the event to all hardware inputs.
3526                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
3527                     if (!serviceState.isHardware || serviceState.service == null) continue;
3528                     try {
3529                         serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
3530                     } catch (RemoteException e) {
3531                         Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
3532                     }
3533                 }
3534             }
3535         }
3536 
3537         @Override
onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo)3538         public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
3539             synchronized (mLock) {
3540                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
3541                 // Broadcast the event to all hardware inputs.
3542                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
3543                     if (!serviceState.isHardware || serviceState.service == null) continue;
3544                     try {
3545                         serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
3546                     } catch (RemoteException e) {
3547                         Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
3548                     }
3549                 }
3550             }
3551         }
3552 
3553         @Override
onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo)3554         public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
3555             synchronized (mLock) {
3556                 Integer state;
3557                 switch (deviceInfo.getDevicePowerStatus()) {
3558                     case HdmiControlManager.POWER_STATUS_ON:
3559                         state = INPUT_STATE_CONNECTED;
3560                         break;
3561                     case HdmiControlManager.POWER_STATUS_STANDBY:
3562                     case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
3563                     case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
3564                         state = INPUT_STATE_CONNECTED_STANDBY;
3565                         break;
3566                     case HdmiControlManager.POWER_STATUS_UNKNOWN:
3567                     default:
3568                         state = null;
3569                         break;
3570                 }
3571                 if (state != null) {
3572                     setStateLocked(inputId, state, mCurrentUserId);
3573                 }
3574                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
3575                 // Broadcast the event to all hardware inputs.
3576                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
3577                     if (!serviceState.isHardware || serviceState.service == null) continue;
3578                     try {
3579                         serviceState.service.notifyHdmiDeviceUpdated(deviceInfo);
3580                     } catch (RemoteException e) {
3581                         Slog.e(TAG, "error in notifyHdmiDeviceUpdated", e);
3582                     }
3583                 }
3584             }
3585         }
3586     }
3587 
3588     private static class SessionNotFoundException extends IllegalArgumentException {
SessionNotFoundException(String name)3589         public SessionNotFoundException(String name) {
3590             super(name);
3591         }
3592     }
3593 
3594     private static class ClientPidNotFoundException extends IllegalArgumentException {
ClientPidNotFoundException(String name)3595         public ClientPidNotFoundException(String name) {
3596             super(name);
3597         }
3598     }
3599 }
3600