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