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