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