blob: 18ed51a6cd5e34ac1ef696920b2c1903f756e38c [file] [log] [blame]
Jae Seo39570912014-02-20 18:23:25 -08001/*
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
17package com.android.server.tv;
18
Wonsik Kim969167d2014-06-24 16:33:17 +090019import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
Wonsik Kime92f8572014-08-12 18:30:24 +090020import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
Wonsik Kim969167d2014-06-24 16:33:17 +090021
Jae Seo4eee6a72016-02-06 11:11:35 +090022import android.annotation.Nullable;
Jae Seo39570912014-02-20 18:23:25 -080023import android.app.ActivityManager;
24import android.content.BroadcastReceiver;
25import android.content.ComponentName;
Jae Seo31dc634be2014-04-15 17:40:23 -070026import android.content.ContentResolver;
27import android.content.ContentUris;
28import android.content.ContentValues;
Jae Seo39570912014-02-20 18:23:25 -080029import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.ServiceConnection;
Jae Seo9c165d62014-08-25 14:39:26 -070033import android.content.pm.ActivityInfo;
Jae Seo39570912014-02-20 18:23:25 -080034import android.content.pm.PackageManager;
Jae Seo8c375fe2015-06-23 20:33:25 -070035import android.content.pm.PackageManager.NameNotFoundException;
Jae Seo39570912014-02-20 18:23:25 -080036import android.content.pm.ResolveInfo;
37import android.content.pm.ServiceInfo;
Youngsang Cho9a22f0f2014-04-09 22:51:54 +090038import android.graphics.Rect;
Wonsik Kime92f8572014-08-12 18:30:24 +090039import android.hardware.hdmi.HdmiControlManager;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090040import android.hardware.hdmi.HdmiDeviceInfo;
Jae Seo4b34cc72015-05-15 17:29:39 -070041import android.media.PlaybackParams;
Jaesung Chung58739e72015-04-24 19:39:59 +090042import android.media.tv.DvbDeviceInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070043import android.media.tv.ITvInputClient;
44import android.media.tv.ITvInputHardware;
45import android.media.tv.ITvInputHardwareCallback;
46import android.media.tv.ITvInputManager;
Wonsik Kim969167d2014-06-24 16:33:17 +090047import android.media.tv.ITvInputManagerCallback;
Jae Seod5cc4a22014-05-30 16:57:43 -070048import android.media.tv.ITvInputService;
49import android.media.tv.ITvInputServiceCallback;
50import android.media.tv.ITvInputSession;
51import android.media.tv.ITvInputSessionCallback;
Jae Seo783645e2014-07-28 17:30:50 +090052import android.media.tv.TvContentRating;
Jae Seo9c165d62014-08-25 14:39:26 -070053import android.media.tv.TvContentRatingSystemInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070054import android.media.tv.TvContract;
55import android.media.tv.TvInputHardwareInfo;
56import android.media.tv.TvInputInfo;
Jae Seo9c165d62014-08-25 14:39:26 -070057import android.media.tv.TvInputManager;
Jae Seod5cc4a22014-05-30 16:57:43 -070058import android.media.tv.TvInputService;
Terry Heoc086a3d2014-06-18 14:26:44 +090059import android.media.tv.TvStreamConfig;
Dongwon Kang1f213912014-07-02 18:35:08 +090060import android.media.tv.TvTrackInfo;
Jae Seo39570912014-02-20 18:23:25 -080061import android.net.Uri;
62import android.os.Binder;
Youngsang Cho832860f2014-05-21 20:54:03 +090063import android.os.Bundle;
Jae Seo31dc634be2014-04-15 17:40:23 -070064import android.os.Handler;
Jae Seo39570912014-02-20 18:23:25 -080065import android.os.IBinder;
Jae Seo31dc634be2014-04-15 17:40:23 -070066import android.os.Looper;
67import android.os.Message;
Jaesung Chung58739e72015-04-24 19:39:59 +090068import android.os.ParcelFileDescriptor;
Jae Seo39570912014-02-20 18:23:25 -080069import android.os.Process;
Kyeongkab.Nam36932052019-03-08 17:53:43 +090070import android.os.RemoteCallbackList;
Jae Seo39570912014-02-20 18:23:25 -080071import android.os.RemoteException;
72import android.os.UserHandle;
Jae Seoc2a89512016-01-28 10:38:11 -080073import android.text.TextUtils;
Youngsang Cho9a22f0f2014-04-09 22:51:54 +090074import android.util.Slog;
Jae Seo39570912014-02-20 18:23:25 -080075import android.util.SparseArray;
Jae Seo6a6059a2014-04-17 21:35:29 -070076import android.view.InputChannel;
Jae Seo39570912014-02-20 18:23:25 -080077import android.view.Surface;
78
79import com.android.internal.content.PackageMonitor;
Jae Seo31dc634be2014-04-15 17:40:23 -070080import com.android.internal.os.SomeArgs;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060081import com.android.internal.util.DumpUtils;
Jaewan Kime14c3f42014-06-27 13:47:48 +090082import com.android.internal.util.IndentingPrintWriter;
Jae Seo31dc634be2014-04-15 17:40:23 -070083import com.android.server.IoThread;
Jae Seo39570912014-02-20 18:23:25 -080084import com.android.server.SystemService;
85
Jaesung Chung58739e72015-04-24 19:39:59 +090086import java.io.File;
Jaewan Kime14c3f42014-06-27 13:47:48 +090087import java.io.FileDescriptor;
Jaesung Chung58739e72015-04-24 19:39:59 +090088import java.io.FileNotFoundException;
Jaewan Kime14c3f42014-06-27 13:47:48 +090089import java.io.PrintWriter;
Jae Seo39570912014-02-20 18:23:25 -080090import java.util.ArrayList;
Chulwoo Lee19ba61a2014-09-03 00:59:35 +090091import java.util.Arrays;
Jaesung Chung58739e72015-04-24 19:39:59 +090092import java.util.Collections;
Jae Seo39570912014-02-20 18:23:25 -080093import java.util.HashMap;
Jae Seo5c80ad22014-06-12 19:52:58 -070094import java.util.HashSet;
Wonsik Kim187423c2014-06-25 14:12:48 +090095import java.util.Iterator;
Jae Seo39570912014-02-20 18:23:25 -080096import java.util.List;
97import java.util.Map;
Jae Seo5c80ad22014-06-12 19:52:58 -070098import java.util.Set;
Jaesung Chung58739e72015-04-24 19:39:59 +090099import java.util.regex.Matcher;
100import java.util.regex.Pattern;
Jae Seo39570912014-02-20 18:23:25 -0800101
102/** This class provides a system service that manages television inputs. */
103public final class TvInputManagerService extends SystemService {
Jae Seoee2ec052014-09-14 10:30:05 -0700104 private static final boolean DEBUG = false;
Jae Seo39570912014-02-20 18:23:25 -0800105 private static final String TAG = "TvInputManagerService";
Jiabinef1659f2016-11-18 10:50:15 +0900106 private static final String DVB_DIRECTORY = "/dev/dvb";
Jae Seo39570912014-02-20 18:23:25 -0800107
Jiabinef1659f2016-11-18 10:50:15 +0900108 // There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
109 // another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
110 // DVB frontend devices from the list of files in the /dev and /dev/dvb/adapter%d directory.
Jaesung Chung58739e72015-04-24 19:39:59 +0900111 private static final Pattern sFrontEndDevicePattern =
112 Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$");
Jiabinef1659f2016-11-18 10:50:15 +0900113 private static final Pattern sAdapterDirPattern =
114 Pattern.compile("^adapter([0-9]+)$");
115 private static final Pattern sFrontEndInAdapterDirPattern =
116 Pattern.compile("^frontend([0-9]+)$");
Jaesung Chung58739e72015-04-24 19:39:59 +0900117
Jae Seo39570912014-02-20 18:23:25 -0800118 private final Context mContext;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000119 private final TvInputHardwareManager mTvInputHardwareManager;
Jae Seo39570912014-02-20 18:23:25 -0800120
121 // A global lock.
122 private final Object mLock = new Object();
123
124 // ID of the current user.
Xiaohui Chen233d94c2015-07-30 15:08:00 -0700125 private int mCurrentUserId = UserHandle.USER_SYSTEM;
Jae Seo39570912014-02-20 18:23:25 -0800126
127 // A map from user id to UserState.
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700128 private final SparseArray<UserState> mUserStates = new SparseArray<>();
Jae Seo39570912014-02-20 18:23:25 -0800129
Jae Seo7eb75df2014-08-08 22:20:48 -0700130 private final WatchLogHandler mWatchLogHandler;
Jae Seo31dc634be2014-04-15 17:40:23 -0700131
Jae Seo39570912014-02-20 18:23:25 -0800132 public TvInputManagerService(Context context) {
133 super(context);
Jae Seo31dc634be2014-04-15 17:40:23 -0700134
Jae Seo39570912014-02-20 18:23:25 -0800135 mContext = context;
Jae Seo8c375fe2015-06-23 20:33:25 -0700136 mWatchLogHandler = new WatchLogHandler(mContext.getContentResolver(),
137 IoThread.get().getLooper());
Wonsik Kim187423c2014-06-25 14:12:48 +0900138 mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
Jae Seo31dc634be2014-04-15 17:40:23 -0700139
Jae Seo39570912014-02-20 18:23:25 -0800140 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700141 getOrCreateUserStateLocked(mCurrentUserId);
Jae Seo39570912014-02-20 18:23:25 -0800142 }
143 }
144
145 @Override
146 public void onStart() {
147 publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
148 }
149
Ji-Hwan Lee0ceb7e42014-06-21 04:42:05 +0900150 @Override
151 public void onBootPhase(int phase) {
152 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
153 registerBroadcastReceivers();
Wonsik Kim187423c2014-06-25 14:12:48 +0900154 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
Ji-Hwan Lee0ceb7e42014-06-21 04:42:05 +0900155 synchronized (mLock) {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900156 buildTvInputListLocked(mCurrentUserId, null);
Jae Seo9c165d62014-08-25 14:39:26 -0700157 buildTvContentRatingSystemListLocked(mCurrentUserId);
Ji-Hwan Lee0ceb7e42014-06-21 04:42:05 +0900158 }
159 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900160 mTvInputHardwareManager.onBootPhase(phase);
Ji-Hwan Lee0ceb7e42014-06-21 04:42:05 +0900161 }
162
Youngsang Cho19c47d72016-05-05 09:21:24 -0700163 @Override
164 public void onUnlockUser(int userHandle) {
165 if (DEBUG) Slog.d(TAG, "onUnlockUser(userHandle=" + userHandle + ")");
166 synchronized (mLock) {
167 if (mCurrentUserId != userHandle) {
168 return;
169 }
170 buildTvInputListLocked(mCurrentUserId, null);
171 buildTvContentRatingSystemListLocked(mCurrentUserId);
172 }
173 }
174
Jae Seo39570912014-02-20 18:23:25 -0800175 private void registerBroadcastReceivers() {
176 PackageMonitor monitor = new PackageMonitor() {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900177 private void buildTvInputList(String[] packages) {
178 synchronized (mLock) {
Dongwon Kanga897e5f2016-04-19 13:39:47 -0700179 if (mCurrentUserId == getChangingUserId()) {
180 buildTvInputListLocked(mCurrentUserId, packages);
181 buildTvContentRatingSystemListLocked(mCurrentUserId);
182 }
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900183 }
184 }
185
186 @Override
187 public void onPackageUpdateFinished(String packageName, int uid) {
188 if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
189 // This callback is invoked when the TV input is reinstalled.
190 // In this case, isReplacing() always returns true.
191 buildTvInputList(new String[] { packageName });
192 }
193
194 @Override
195 public void onPackagesAvailable(String[] packages) {
196 if (DEBUG) {
197 Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
198 }
199 // This callback is invoked when the media on which some packages exist become
200 // available.
201 if (isReplacing()) {
202 buildTvInputList(packages);
203 }
204 }
205
206 @Override
207 public void onPackagesUnavailable(String[] packages) {
208 // This callback is invoked when the media on which some packages exist become
209 // unavailable.
210 if (DEBUG) {
211 Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
212 + ")");
213 }
214 if (isReplacing()) {
215 buildTvInputList(packages);
216 }
217 }
218
Jae Seo39570912014-02-20 18:23:25 -0800219 @Override
220 public void onSomePackagesChanged() {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900221 // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
222 // the TV inputs.
Dongwon Kang426c9a42014-08-26 17:39:21 +0900223 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900224 if (isReplacing()) {
225 if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
226 // When the package is updated, buildTvInputListLocked is called in other
227 // methods instead.
228 return;
Jae Seo39570912014-02-20 18:23:25 -0800229 }
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900230 buildTvInputList(null);
Jae Seo39570912014-02-20 18:23:25 -0800231 }
Jae Seo5c80ad22014-06-12 19:52:58 -0700232
233 @Override
Dongwon Kang31a8f842015-04-08 18:26:23 -0700234 public boolean onPackageChanged(String packageName, int uid, String[] components) {
235 // The input list needs to be updated in any cases, regardless of whether
236 // it happened to the whole package or a specific component. Returning true so that
237 // the update can be handled in {@link #onSomePackagesChanged}.
238 return true;
239 }
Jae Seo39570912014-02-20 18:23:25 -0800240 };
241 monitor.register(mContext, null, UserHandle.ALL, true);
242
243 IntentFilter intentFilter = new IntentFilter();
244 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
245 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
246 mContext.registerReceiverAsUser(new BroadcastReceiver() {
247 @Override
248 public void onReceive(Context context, Intent intent) {
249 String action = intent.getAction();
250 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
251 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
252 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
253 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
254 }
255 }
256 }, UserHandle.ALL, intentFilter, null, null);
257 }
258
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900259 private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900260 return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE,
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900261 component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
Wonsik Kim187423c2014-06-25 14:12:48 +0900262 }
263
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900264 private void buildTvInputListLocked(int userId, String[] updatedPackages) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700265 UserState userState = getOrCreateUserStateLocked(userId);
Wonsik Kim969167d2014-06-24 16:33:17 +0900266 userState.packageSet.clear();
Jae Seo39570912014-02-20 18:23:25 -0800267
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900268 if (DEBUG) Slog.d(TAG, "buildTvInputList");
Jae Seo39570912014-02-20 18:23:25 -0800269 PackageManager pm = mContext.getPackageManager();
Jae Seo76976fa2015-05-20 21:51:16 -0700270 List<ResolveInfo> services = pm.queryIntentServicesAsUser(
Chulwoo Leee7bb7d62014-05-27 14:10:37 +0900271 new Intent(TvInputService.SERVICE_INTERFACE),
Jae Seo76976fa2015-05-20 21:51:16 -0700272 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
273 userId);
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700274 List<TvInputInfo> inputList = new ArrayList<>();
Jae Seo39570912014-02-20 18:23:25 -0800275 for (ResolveInfo ri : services) {
276 ServiceInfo si = ri.serviceInfo;
277 if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +0900278 Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
Jae Seo39570912014-02-20 18:23:25 -0800279 + android.Manifest.permission.BIND_TV_INPUT);
280 continue;
281 }
Jae Seo9cc28e52014-08-12 16:45:58 -0700282
283 ComponentName component = new ComponentName(si.packageName, si.name);
284 if (hasHardwarePermission(pm, component)) {
285 ServiceState serviceState = userState.serviceStateMap.get(component);
286 if (serviceState == null) {
Jae Seoc980f43d2016-02-09 23:46:58 -0800287 // New hardware input found. Create a new ServiceState and connect to the
288 // service to populate the hardware list.
Jae Seo9cc28e52014-08-12 16:45:58 -0700289 serviceState = new ServiceState(component, userId);
290 userState.serviceStateMap.put(component, serviceState);
Wonsik Kimf271eac2014-08-30 12:55:10 +0900291 updateServiceConnectionLocked(component, userId);
Wonsik Kim187423c2014-06-25 14:12:48 +0900292 } else {
Shubang71d5c762017-02-23 15:46:40 -0800293 inputList.addAll(serviceState.hardwareInputMap.values());
Jae Seo9cc28e52014-08-12 16:45:58 -0700294 }
295 } else {
296 try {
Jae Seoc03671f2016-01-26 15:15:22 -0800297 TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
298 inputList.add(info);
Jae Seo18c0cfb2016-05-16 18:19:11 -0700299 } catch (Exception e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700300 Slog.e(TAG, "failed to load TV input " + si.name, e);
Jae Seo9cc28e52014-08-12 16:45:58 -0700301 continue;
Wonsik Kim969167d2014-06-24 16:33:17 +0900302 }
Chulwoo Leee7bb7d62014-05-27 14:10:37 +0900303 }
Jae Seo9cc28e52014-08-12 16:45:58 -0700304 userState.packageSet.add(si.packageName);
305 }
306
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700307 Map<String, TvInputState> inputMap = new HashMap<>();
Jae Seo9cc28e52014-08-12 16:45:58 -0700308 for (TvInputInfo info : inputList) {
Jae Seofea8dd42014-08-26 13:57:41 -0700309 if (DEBUG) {
310 Slog.d(TAG, "add " + info.getId());
311 }
Jae Seoabda4202016-01-28 19:13:04 -0800312 TvInputState inputState = userState.inputMap.get(info.getId());
313 if (inputState == null) {
314 inputState = new TvInputState();
Jae Seo9cc28e52014-08-12 16:45:58 -0700315 }
Shubang2263fb02016-05-13 16:51:53 -0700316 inputState.info = info;
Jae Seoabda4202016-01-28 19:13:04 -0800317 inputMap.put(info.getId(), inputState);
Jae Seo39570912014-02-20 18:23:25 -0800318 }
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900319
320 for (String inputId : inputMap.keySet()) {
321 if (!userState.inputMap.containsKey(inputId)) {
322 notifyInputAddedLocked(userState, inputId);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900323 } else if (updatedPackages != null) {
324 // Notify the package updates
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +0900325 ComponentName component = inputMap.get(inputId).info.getComponent();
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900326 for (String updatedPackage : updatedPackages) {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +0900327 if (component.getPackageName().equals(updatedPackage)) {
328 updateServiceConnectionLocked(component, userId);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900329 notifyInputUpdatedLocked(userState, inputId);
330 break;
331 }
332 }
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900333 }
334 }
335
336 for (String inputId : userState.inputMap.keySet()) {
337 if (!inputMap.containsKey(inputId)) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900338 TvInputInfo info = userState.inputMap.get(inputId).info;
Dongwon Kang426c9a42014-08-26 17:39:21 +0900339 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
340 if (serviceState != null) {
341 abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
342 }
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900343 notifyInputRemovedLocked(userState, inputId);
344 }
345 }
346
347 userState.inputMap.clear();
348 userState.inputMap = inputMap;
Jae Seo9c165d62014-08-25 14:39:26 -0700349 }
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900350
Jae Seo9c165d62014-08-25 14:39:26 -0700351 private void buildTvContentRatingSystemListLocked(int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700352 UserState userState = getOrCreateUserStateLocked(userId);
Jae Seo9c165d62014-08-25 14:39:26 -0700353 userState.contentRatingSystemList.clear();
354
355 final PackageManager pm = mContext.getPackageManager();
356 Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS);
357 for (ResolveInfo resolveInfo :
358 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) {
359 ActivityInfo receiver = resolveInfo.activityInfo;
360 Bundle metaData = receiver.metaData;
361 if (metaData == null) {
362 continue;
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900363 }
Jae Seo9c165d62014-08-25 14:39:26 -0700364
365 int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS);
366 if (xmlResId == 0) {
367 Slog.w(TAG, "Missing meta-data '"
368 + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver "
369 + receiver.packageName + "/" + receiver.name);
370 continue;
371 }
372 userState.contentRatingSystemList.add(
373 TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId,
374 receiver.applicationInfo));
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900375 }
Jae Seo39570912014-02-20 18:23:25 -0800376 }
377
378 private void switchUser(int userId) {
379 synchronized (mLock) {
380 if (mCurrentUserId == userId) {
381 return;
382 }
shubang8049f202016-04-25 11:21:42 -0700383 UserState userState = mUserStates.get(mCurrentUserId);
384 List<SessionState> sessionStatesToRelease = new ArrayList<>();
385 for (SessionState sessionState : userState.sessionStateMap.values()) {
386 if (sessionState.session != null && !sessionState.isRecordingSession) {
387 sessionStatesToRelease.add(sessionState);
388 }
389 }
390 for (SessionState sessionState : sessionStatesToRelease) {
391 try {
392 sessionState.session.release();
393 } catch (RemoteException e) {
394 Slog.e(TAG, "error in release", e);
395 }
396 clearSessionAndNotifyClientLocked(sessionState);
397 }
398
399 for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator();
400 it.hasNext(); ) {
401 ComponentName component = it.next();
402 ServiceState serviceState = userState.serviceStateMap.get(component);
403 if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
404 if (serviceState.callback != null) {
405 try {
406 serviceState.service.unregisterCallback(serviceState.callback);
407 } catch (RemoteException e) {
408 Slog.e(TAG, "error in unregisterCallback", e);
409 }
410 }
411 mContext.unbindService(serviceState.connection);
412 it.remove();
413 }
414 }
Jae Seo39570912014-02-20 18:23:25 -0800415
Jae Seo8c375fe2015-06-23 20:33:25 -0700416 mCurrentUserId = userId;
Jae Seo4f1a6d42015-07-20 16:15:01 -0700417 getOrCreateUserStateLocked(userId);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900418 buildTvInputListLocked(userId, null);
Jae Seo9c165d62014-08-25 14:39:26 -0700419 buildTvContentRatingSystemListLocked(userId);
Jae Seo8c375fe2015-06-23 20:33:25 -0700420 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_SWITCH_CONTENT_RESOLVER,
421 getContentResolverForUser(userId)).sendToTarget();
Jae Seo39570912014-02-20 18:23:25 -0800422 }
423 }
424
shubang8049f202016-04-25 11:21:42 -0700425 private void clearSessionAndNotifyClientLocked(SessionState state) {
426 if (state.client != null) {
427 try {
428 state.client.onSessionReleased(state.seq);
429 } catch(RemoteException e) {
430 Slog.e(TAG, "error in onSessionReleased", e);
431 }
432 }
433 // If there are any other sessions based on this session, they should be released.
434 UserState userState = getOrCreateUserStateLocked(state.userId);
435 for (SessionState sessionState : userState.sessionStateMap.values()) {
436 if (state.sessionToken == sessionState.hardwareSessionToken) {
437 releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID, state.userId);
438 try {
439 sessionState.client.onSessionReleased(sessionState.seq);
440 } catch (RemoteException e) {
441 Slog.e(TAG, "error in onSessionReleased", e);
442 }
443 }
444 }
445 removeSessionStateLocked(state.sessionToken, state.userId);
446 }
447
Jae Seo39570912014-02-20 18:23:25 -0800448 private void removeUser(int userId) {
449 synchronized (mLock) {
Jae Seob06cb882014-04-09 12:08:17 -0700450 UserState userState = mUserStates.get(userId);
451 if (userState == null) {
452 return;
453 }
shubang8049f202016-04-25 11:21:42 -0700454 // Release all created sessions.
455 for (SessionState state : userState.sessionStateMap.values()) {
456 if (state.session != null) {
457 try {
458 state.session.release();
459 } catch (RemoteException e) {
460 Slog.e(TAG, "error in release", e);
461 }
462 }
463 }
464 userState.sessionStateMap.clear();
465
466 // Unregister all callbacks and unbind all services.
467 for (ServiceState serviceState : userState.serviceStateMap.values()) {
468 if (serviceState.service != null) {
469 if (serviceState.callback != null) {
470 try {
471 serviceState.service.unregisterCallback(serviceState.callback);
472 } catch (RemoteException e) {
473 Slog.e(TAG, "error in unregisterCallback", e);
474 }
475 }
476 mContext.unbindService(serviceState.connection);
477 }
478 }
479 userState.serviceStateMap.clear();
Jae Seo39570912014-02-20 18:23:25 -0800480
Jae Seofea8dd42014-08-26 13:57:41 -0700481 // Clear everything else.
482 userState.inputMap.clear();
483 userState.packageSet.clear();
Jae Seo9c165d62014-08-25 14:39:26 -0700484 userState.contentRatingSystemList.clear();
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900485 userState.clientStateMap.clear();
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900486 userState.mCallbacks.kill();
Jae Seofea8dd42014-08-26 13:57:41 -0700487 userState.mainSessionToken = null;
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900488
Jae Seo39570912014-02-20 18:23:25 -0800489 mUserStates.remove(userId);
490 }
491 }
492
Jae Seo8c375fe2015-06-23 20:33:25 -0700493 private ContentResolver getContentResolverForUser(int userId) {
494 UserHandle user = new UserHandle(userId);
495 Context context;
496 try {
497 context = mContext.createPackageContextAsUser("android", 0, user);
498 } catch (NameNotFoundException e) {
Jae Seo2a2b2992016-01-12 23:13:14 -0800499 Slog.e(TAG, "failed to create package context as user " + user);
Jae Seo8c375fe2015-06-23 20:33:25 -0700500 context = mContext;
501 }
502 return context.getContentResolver();
503 }
504
Jae Seo4f1a6d42015-07-20 16:15:01 -0700505 private UserState getOrCreateUserStateLocked(int userId) {
Jae Seo39570912014-02-20 18:23:25 -0800506 UserState userState = mUserStates.get(userId);
507 if (userState == null) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700508 userState = new UserState(mContext, userId);
509 mUserStates.put(userId, userState);
Jae Seo39570912014-02-20 18:23:25 -0800510 }
511 return userState;
512 }
513
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900514 private ServiceState getServiceStateLocked(ComponentName component, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700515 UserState userState = getOrCreateUserStateLocked(userId);
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900516 ServiceState serviceState = userState.serviceStateMap.get(component);
Jae Seo39570912014-02-20 18:23:25 -0800517 if (serviceState == null) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900518 throw new IllegalStateException("Service state not found for " + component + " (userId="
Sungsoo Lim7de5e232014-04-12 16:51:27 +0900519 + userId + ")");
Jae Seo39570912014-02-20 18:23:25 -0800520 }
521 return serviceState;
522 }
523
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900524 private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700525 UserState userState = getOrCreateUserStateLocked(userId);
Jae Seo39570912014-02-20 18:23:25 -0800526 SessionState sessionState = userState.sessionStateMap.get(sessionToken);
527 if (sessionState == null) {
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900528 throw new SessionNotFoundException("Session state not found for token " + sessionToken);
Jae Seo39570912014-02-20 18:23:25 -0800529 }
530 // Only the application that requested this session or the system can access it.
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900531 if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
Jae Seo39570912014-02-20 18:23:25 -0800532 throw new SecurityException("Illegal access to the session with token " + sessionToken
533 + " from uid " + callingUid);
534 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900535 return sessionState;
536 }
537
538 private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
Ji-Hwan Lee4c526972014-07-22 04:46:30 +0900539 return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
540 }
541
542 private ITvInputSession getSessionLocked(SessionState sessionState) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900543 ITvInputSession session = sessionState.session;
Jae Seo39570912014-02-20 18:23:25 -0800544 if (session == null) {
Ji-Hwan Lee4c526972014-07-22 04:46:30 +0900545 throw new IllegalStateException("Session not yet created for token "
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900546 + sessionState.sessionToken);
Jae Seo39570912014-02-20 18:23:25 -0800547 }
548 return session;
549 }
550
551 private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
552 String methodName) {
553 return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
554 false, methodName, null);
555 }
556
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900557 private void updateServiceConnectionLocked(ComponentName component, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700558 UserState userState = getOrCreateUserStateLocked(userId);
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900559 ServiceState serviceState = userState.serviceStateMap.get(component);
Jae Seo39570912014-02-20 18:23:25 -0800560 if (serviceState == null) {
561 return;
562 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900563 if (serviceState.reconnecting) {
564 if (!serviceState.sessionTokens.isEmpty()) {
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900565 // wait until all the sessions are removed.
566 return;
567 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900568 serviceState.reconnecting = false;
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900569 }
shubang8049f202016-04-25 11:21:42 -0700570
571 boolean shouldBind;
572 if (userId == mCurrentUserId) {
573 shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
574 } else {
575 // For a non-current user,
576 // if sessionTokens is not empty, it contains recording sessions only
577 // because other sessions must have been removed while switching user
578 // and non-recording sessions are not created by createSession().
579 shouldBind = !serviceState.sessionTokens.isEmpty();
580 }
581
582 if (serviceState.service == null && shouldBind) {
Jae Seo39570912014-02-20 18:23:25 -0800583 // This means that the service is not yet connected but its state indicates that we
584 // have pending requests. Then, connect the service.
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900585 if (serviceState.bound) {
Jae Seo39570912014-02-20 18:23:25 -0800586 // We have already bound to the service so we don't try to bind again until after we
587 // unbind later on.
588 return;
589 }
590 if (DEBUG) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900591 Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
Jae Seo39570912014-02-20 18:23:25 -0800592 }
Sungsoo Limd6672b52014-04-30 10:43:26 +0900593
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900594 Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900595 serviceState.bound = mContext.bindServiceAsUser(
Dianne Hackbornd69e4c12015-04-24 09:54:54 -0700596 i, serviceState.connection,
597 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
598 new UserHandle(userId));
shubang8049f202016-04-25 11:21:42 -0700599 } else if (serviceState.service != null && !shouldBind) {
Jae Seo39570912014-02-20 18:23:25 -0800600 // This means that the service is already connected but its state indicates that we have
601 // nothing to do with it. Then, disconnect the service.
602 if (DEBUG) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900603 Slog.d(TAG, "unbindService(service=" + component + ")");
Jae Seo39570912014-02-20 18:23:25 -0800604 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900605 mContext.unbindService(serviceState.connection);
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900606 userState.serviceStateMap.remove(component);
Jae Seo39570912014-02-20 18:23:25 -0800607 }
608 }
609
Dongwon Kang426c9a42014-08-26 17:39:21 +0900610 private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
611 String inputId, int userId) {
612 // Let clients know the create session requests are failed.
Jae Seo4f1a6d42015-07-20 16:15:01 -0700613 UserState userState = getOrCreateUserStateLocked(userId);
Dongwon Kangf7f49dd2014-08-27 20:48:22 +0900614 List<SessionState> sessionsToAbort = new ArrayList<>();
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900615 for (IBinder sessionToken : serviceState.sessionTokens) {
Dongwon Kang426c9a42014-08-26 17:39:21 +0900616 SessionState sessionState = userState.sessionStateMap.get(sessionToken);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900617 if (sessionState.session == null && (inputId == null
Jae Seo2cdb05e2016-02-04 22:17:13 +0900618 || sessionState.inputId.equals(inputId))) {
Dongwon Kangf7f49dd2014-08-27 20:48:22 +0900619 sessionsToAbort.add(sessionState);
Dongwon Kang426c9a42014-08-26 17:39:21 +0900620 }
621 }
Dongwon Kangf7f49dd2014-08-27 20:48:22 +0900622 for (SessionState sessionState : sessionsToAbort) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900623 removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
624 sendSessionTokenToClientLocked(sessionState.client,
Jae Seo2cdb05e2016-02-04 22:17:13 +0900625 sessionState.inputId, null, null, sessionState.seq);
Dongwon Kangf7f49dd2014-08-27 20:48:22 +0900626 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900627 updateServiceConnectionLocked(serviceState.component, userId);
Dongwon Kang426c9a42014-08-26 17:39:21 +0900628 }
629
shubangaf2fb662018-11-09 17:03:46 -0800630 private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900631 int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700632 UserState userState = getOrCreateUserStateLocked(userId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900633 SessionState sessionState = userState.sessionStateMap.get(sessionToken);
Jae Seo39570912014-02-20 18:23:25 -0800634 if (DEBUG) {
Jae Seo2cdb05e2016-02-04 22:17:13 +0900635 Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")");
Jae Seo39570912014-02-20 18:23:25 -0800636 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900637 InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
Jae Seo6a6059a2014-04-17 21:35:29 -0700638
Jae Seo39570912014-02-20 18:23:25 -0800639 // Set up a callback to send the session token.
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900640 ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
Jae Seo39570912014-02-20 18:23:25 -0800641
shubangaf2fb662018-11-09 17:03:46 -0800642 boolean created = true;
Jae Seo39570912014-02-20 18:23:25 -0800643 // Create a session. When failed, send a null token immediately.
644 try {
Jae Seoa826d012016-01-18 13:03:35 -0800645 if (sessionState.isRecordingSession) {
Jae Seo2cdb05e2016-02-04 22:17:13 +0900646 service.createRecordingSession(callback, sessionState.inputId);
Jae Seoa826d012016-01-18 13:03:35 -0800647 } else {
Jae Seo2cdb05e2016-02-04 22:17:13 +0900648 service.createSession(channels[1], callback, sessionState.inputId);
Jae Seoa826d012016-01-18 13:03:35 -0800649 }
Jae Seo39570912014-02-20 18:23:25 -0800650 } catch (RemoteException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +0900651 Slog.e(TAG, "error in createSession", e);
Jae Seo2cdb05e2016-02-04 22:17:13 +0900652 sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900653 null, sessionState.seq);
shubangaf2fb662018-11-09 17:03:46 -0800654 created = false;
Jae Seo39570912014-02-20 18:23:25 -0800655 }
Jae Seo6a6059a2014-04-17 21:35:29 -0700656 channels[1].dispose();
shubangaf2fb662018-11-09 17:03:46 -0800657 return created;
Jae Seo39570912014-02-20 18:23:25 -0800658 }
659
Sungsoo Limd6672b52014-04-30 10:43:26 +0900660 private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
Jae Seo5c80ad22014-06-12 19:52:58 -0700661 IBinder sessionToken, InputChannel channel, int seq) {
Jae Seo39570912014-02-20 18:23:25 -0800662 try {
Sungsoo Limd6672b52014-04-30 10:43:26 +0900663 client.onSessionCreated(inputId, sessionToken, channel, seq);
Jae Seofea8dd42014-08-26 13:57:41 -0700664 } catch (RemoteException e) {
665 Slog.e(TAG, "error in onSessionCreated", e);
Jae Seo39570912014-02-20 18:23:25 -0800666 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900667 }
Jae Seo39570912014-02-20 18:23:25 -0800668
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900669 private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900670 SessionState sessionState = null;
671 try {
672 sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
673 if (sessionState.session != null) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700674 UserState userState = getOrCreateUserStateLocked(userId);
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900675 if (sessionToken == userState.mainSessionToken) {
676 setMainLocked(sessionToken, false, callingUid, userId);
677 }
Kyeongkab.Nam8d6b71a2019-01-16 10:44:37 +0900678 sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
Jae Seoe3c11e82016-02-08 23:18:49 -0800679 sessionState.session.release();
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900680 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900681 } catch (RemoteException | SessionNotFoundException e) {
682 Slog.e(TAG, "error in releaseSession", e);
683 } finally {
684 if (sessionState != null) {
685 sessionState.session = null;
686 }
Jae Seo39570912014-02-20 18:23:25 -0800687 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900688 removeSessionStateLocked(sessionToken, userId);
Jae Seo39570912014-02-20 18:23:25 -0800689 }
690
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900691 private void removeSessionStateLocked(IBinder sessionToken, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700692 UserState userState = getOrCreateUserStateLocked(userId);
Ji-Hwan Leeabca0ee2014-07-24 17:34:19 +0900693 if (sessionToken == userState.mainSessionToken) {
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900694 if (DEBUG) {
695 Slog.d(TAG, "mainSessionToken=null");
696 }
Ji-Hwan Leeabca0ee2014-07-24 17:34:19 +0900697 userState.mainSessionToken = null;
698 }
699
700 // Remove the session state from the global session state map of the current user.
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900701 SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
702
Chulwoo Lee8d4ded02014-07-10 03:56:39 +0900703 if (sessionState == null) {
704 return;
705 }
706
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900707 // Also remove the session token from the session token list of the current client and
708 // service.
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900709 ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900710 if (clientState != null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900711 clientState.sessionTokens.remove(sessionToken);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900712 if (clientState.isEmpty()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900713 userState.clientStateMap.remove(sessionState.client.asBinder());
Kyeongkab.Nam8d6b71a2019-01-16 10:44:37 +0900714 sessionState.client.asBinder().unlinkToDeath(clientState, 0);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900715 }
716 }
717
Jae Seo2cdb05e2016-02-04 22:17:13 +0900718 ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName);
719 if (serviceState != null) {
720 serviceState.sessionTokens.remove(sessionToken);
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900721 }
Jae Seo2cdb05e2016-02-04 22:17:13 +0900722 updateServiceConnectionLocked(sessionState.componentName, userId);
Jae Seo7eb75df2014-08-08 22:20:48 -0700723
724 // Log the end of watch.
725 SomeArgs args = SomeArgs.obtain();
726 args.arg1 = sessionToken;
727 args.arg2 = System.currentTimeMillis();
728 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget();
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900729 }
730
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900731 private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900732 try {
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900733 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
734 if (sessionState.hardwareSessionToken != null) {
735 sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
736 Process.SYSTEM_UID, userId);
737 }
Jae Seo2cdb05e2016-02-04 22:17:13 +0900738 ServiceState serviceState = getServiceStateLocked(sessionState.componentName, userId);
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900739 if (!serviceState.isHardware) {
740 return;
741 }
742 ITvInputSession session = getSessionLocked(sessionState);
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900743 session.setMain(isMain);
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900744 } catch (RemoteException | SessionNotFoundException e) {
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900745 Slog.e(TAG, "error in setMain", e);
746 }
747 }
748
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900749 private void notifyInputAddedLocked(UserState userState, String inputId) {
750 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -0700751 Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")");
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900752 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900753 int n = userState.mCallbacks.beginBroadcast();
754 for (int i = 0; i < n; ++i) {
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900755 try {
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900756 userState.mCallbacks.getBroadcastItem(i).onInputAdded(inputId);
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900757 } catch (RemoteException e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700758 Slog.e(TAG, "failed to report added input to callback", e);
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900759 }
760 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900761 userState.mCallbacks.finishBroadcast();
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900762 }
763
764 private void notifyInputRemovedLocked(UserState userState, String inputId) {
765 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -0700766 Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")");
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900767 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900768 int n = userState.mCallbacks.beginBroadcast();
769 for (int i = 0; i < n; ++i) {
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900770 try {
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900771 userState.mCallbacks.getBroadcastItem(i).onInputRemoved(inputId);
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900772 } catch (RemoteException e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700773 Slog.e(TAG, "failed to report removed input to callback", e);
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900774 }
775 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900776 userState.mCallbacks.finishBroadcast();
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900777 }
778
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900779 private void notifyInputUpdatedLocked(UserState userState, String inputId) {
780 if (DEBUG) {
781 Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")");
782 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900783 int n = userState.mCallbacks.beginBroadcast();
784 for (int i = 0; i < n; ++i) {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900785 try {
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900786 userState.mCallbacks.getBroadcastItem(i).onInputUpdated(inputId);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900787 } catch (RemoteException e) {
788 Slog.e(TAG, "failed to report updated input to callback", e);
789 }
790 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900791 userState.mCallbacks.finishBroadcast();
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900792 }
793
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900794 private void notifyInputStateChangedLocked(UserState userState, String inputId,
Wonsik Kim969167d2014-06-24 16:33:17 +0900795 int state, ITvInputManagerCallback targetCallback) {
796 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -0700797 Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId
798 + ", state=" + state + ")");
Wonsik Kim969167d2014-06-24 16:33:17 +0900799 }
800 if (targetCallback == null) {
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900801 int n = userState.mCallbacks.beginBroadcast();
802 for (int i = 0; i < n; ++i) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900803 try {
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900804 userState.mCallbacks.getBroadcastItem(i).onInputStateChanged(inputId, state);
Wonsik Kim969167d2014-06-24 16:33:17 +0900805 } catch (RemoteException e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700806 Slog.e(TAG, "failed to report state change to callback", e);
Wonsik Kim969167d2014-06-24 16:33:17 +0900807 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900808 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900809 userState.mCallbacks.finishBroadcast();
Wonsik Kim969167d2014-06-24 16:33:17 +0900810 } else {
811 try {
812 targetCallback.onInputStateChanged(inputId, state);
813 } catch (RemoteException e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700814 Slog.e(TAG, "failed to report state change to callback", e);
Wonsik Kim969167d2014-06-24 16:33:17 +0900815 }
816 }
817 }
818
Jae Seoaa5605f2016-02-13 01:38:08 -0800819 private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) {
Jae Seoa826d012016-01-18 13:03:35 -0800820 if (DEBUG) {
Jae Seoaa5605f2016-02-13 01:38:08 -0800821 Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")");
Jae Seoa826d012016-01-18 13:03:35 -0800822 }
Jae Seoabda4202016-01-28 19:13:04 -0800823 String inputId = inputInfo.getId();
824 TvInputState inputState = userState.inputMap.get(inputId);
825 if (inputState == null) {
826 Slog.e(TAG, "failed to set input info - unknown input id " + inputId);
827 return;
828 }
Jae Seoabda4202016-01-28 19:13:04 -0800829 inputState.info = inputInfo;
830
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900831 int n = userState.mCallbacks.beginBroadcast();
832 for (int i = 0; i < n; ++i) {
Jae Seoa826d012016-01-18 13:03:35 -0800833 try {
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900834 userState.mCallbacks.getBroadcastItem(i).onTvInputInfoUpdated(inputInfo);
Jae Seoa826d012016-01-18 13:03:35 -0800835 } catch (RemoteException e) {
Jae Seoaa5605f2016-02-13 01:38:08 -0800836 Slog.e(TAG, "failed to report updated input info to callback", e);
Jae Seoa826d012016-01-18 13:03:35 -0800837 }
838 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +0900839 userState.mCallbacks.finishBroadcast();
Jae Seoa826d012016-01-18 13:03:35 -0800840 }
841
Wonsik Kim969167d2014-06-24 16:33:17 +0900842 private void setStateLocked(String inputId, int state, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700843 UserState userState = getOrCreateUserStateLocked(userId);
Wonsik Kim969167d2014-06-24 16:33:17 +0900844 TvInputState inputState = userState.inputMap.get(inputId);
Baohe.Wang7af0ff92019-06-30 22:34:16 +0800845 if (inputState == null) {
846 Slog.e(TAG, "failed to setStateLocked - unknown input id " + inputId);
847 return;
848 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900849 ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
850 int oldState = inputState.state;
851 inputState.state = state;
852 if (serviceState != null && serviceState.service == null
shubang8049f202016-04-25 11:21:42 -0700853 && (!serviceState.sessionTokens.isEmpty() || serviceState.isHardware)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900854 // We don't notify state change while reconnecting. It should remain disconnected.
855 return;
856 }
857 if (oldState != state) {
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900858 notifyInputStateChangedLocked(userState, inputId, state, null);
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900859 }
860 }
861
Jae Seo39570912014-02-20 18:23:25 -0800862 private final class BinderService extends ITvInputManager.Stub {
863 @Override
864 public List<TvInputInfo> getTvInputList(int userId) {
865 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
866 Binder.getCallingUid(), userId, "getTvInputList");
867 final long identity = Binder.clearCallingIdentity();
868 try {
869 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700870 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700871 List<TvInputInfo> inputList = new ArrayList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900872 for (TvInputState state : userState.inputMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900873 inputList.add(state.info);
Jae Seo39570912014-02-20 18:23:25 -0800874 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900875 return inputList;
Jae Seo39570912014-02-20 18:23:25 -0800876 }
877 } finally {
878 Binder.restoreCallingIdentity(identity);
879 }
Jae Seo39570912014-02-20 18:23:25 -0800880 }
881
882 @Override
Jae Seob3758052014-07-12 19:25:24 -0700883 public TvInputInfo getTvInputInfo(String inputId, int userId) {
884 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
885 Binder.getCallingUid(), userId, "getTvInputInfo");
886 final long identity = Binder.clearCallingIdentity();
887 try {
888 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700889 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seob3758052014-07-12 19:25:24 -0700890 TvInputState state = userState.inputMap.get(inputId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900891 return state == null ? null : state.info;
Jae Seob3758052014-07-12 19:25:24 -0700892 }
893 } finally {
894 Binder.restoreCallingIdentity(identity);
895 }
896 }
897
Jae Seoaa5605f2016-02-13 01:38:08 -0800898 public void updateTvInputInfo(TvInputInfo inputInfo, int userId) {
Jae Seoc2a89512016-01-28 10:38:11 -0800899 String inputInfoPackageName = inputInfo.getServiceInfo().packageName;
900 String callingPackageName = getCallingPackageName();
Dongwon Kang9d0e0f12016-10-11 15:14:01 -0700901 if (!TextUtils.equals(inputInfoPackageName, callingPackageName)
902 && mContext.checkCallingPermission(
903 android.Manifest.permission.WRITE_SECURE_SETTINGS)
904 != PackageManager.PERMISSION_GRANTED) {
905 // Only the app owning the input and system settings are allowed to update info.
Jae Seoc2a89512016-01-28 10:38:11 -0800906 throw new IllegalArgumentException("calling package " + callingPackageName
907 + " is not allowed to change TvInputInfo for " + inputInfoPackageName);
908 }
909
910 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Jae Seoaa5605f2016-02-13 01:38:08 -0800911 Binder.getCallingUid(), userId, "updateTvInputInfo");
Jae Seoc2a89512016-01-28 10:38:11 -0800912 final long identity = Binder.clearCallingIdentity();
913 try {
914 synchronized (mLock) {
915 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seoaa5605f2016-02-13 01:38:08 -0800916 updateTvInputInfoLocked(userState, inputInfo);
Jae Seoc2a89512016-01-28 10:38:11 -0800917 }
918 } finally {
919 Binder.restoreCallingIdentity(identity);
920 }
921 }
922
923 private String getCallingPackageName() {
924 final String[] packages = mContext.getPackageManager().getPackagesForUid(
925 Binder.getCallingUid());
926 if (packages != null && packages.length > 0) {
927 return packages[0];
928 }
929 return "unknown";
930 }
931
Jae Seob3758052014-07-12 19:25:24 -0700932 @Override
Dongwon Kang993f81e2014-11-27 19:34:18 +0900933 public int getTvInputState(String inputId, int userId) {
934 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
935 Binder.getCallingUid(), userId, "getTvInputState");
936 final long identity = Binder.clearCallingIdentity();
937 try {
938 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700939 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Dongwon Kang993f81e2014-11-27 19:34:18 +0900940 TvInputState state = userState.inputMap.get(inputId);
Jae Seo82fce642015-04-20 15:37:50 -0700941 return state == null ? INPUT_STATE_CONNECTED : state.state;
Dongwon Kang993f81e2014-11-27 19:34:18 +0900942 }
943 } finally {
944 Binder.restoreCallingIdentity(identity);
945 }
946 }
947
948 @Override
Jae Seo9c165d62014-08-25 14:39:26 -0700949 public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) {
Shubangdd3ec0b2017-06-23 13:54:10 -0700950 if (mContext.checkCallingPermission(
951 android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS)
952 != PackageManager.PERMISSION_GRANTED) {
953 throw new SecurityException(
954 "The caller does not have permission to read content rating systems");
955 }
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900956 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Jae Seo9c165d62014-08-25 14:39:26 -0700957 Binder.getCallingUid(), userId, "getTvContentRatingSystemList");
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900958 final long identity = Binder.clearCallingIdentity();
959 try {
960 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700961 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo9c165d62014-08-25 14:39:26 -0700962 return userState.contentRatingSystemList;
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900963 }
964 } finally {
965 Binder.restoreCallingIdentity(identity);
966 }
967 }
968
969 @Override
Conrad Chen558acf92017-04-05 17:31:01 -0700970 public void sendTvInputNotifyIntent(Intent intent, int userId) {
971 if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
972 != PackageManager.PERMISSION_GRANTED) {
973 throw new SecurityException("The caller: " + getCallingPackageName()
974 + " doesn't have permission: "
975 + android.Manifest.permission.NOTIFY_TV_INPUTS);
976 }
977 if (TextUtils.isEmpty(intent.getPackage())) {
978 throw new IllegalArgumentException("Must specify package name to notify.");
979 }
980 switch (intent.getAction()) {
981 case TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
982 if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
983 throw new IllegalArgumentException("Invalid preview program ID.");
984 }
985 break;
986 case TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
987 if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
988 throw new IllegalArgumentException("Invalid watch next program ID.");
989 }
990 break;
991 case TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
992 if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
993 throw new IllegalArgumentException("Invalid preview program ID.");
994 }
995 if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
996 throw new IllegalArgumentException("Invalid watch next program ID.");
997 }
998 break;
999 default:
1000 throw new IllegalArgumentException("Invalid TV input notifying action: "
1001 + intent.getAction());
1002 }
1003 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1004 Binder.getCallingUid(), userId, "sendTvInputNotifyIntent");
1005 final long identity = Binder.clearCallingIdentity();
1006 try {
1007 getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
1008 } finally {
1009 Binder.restoreCallingIdentity(identity);
1010 }
1011 }
1012
1013 @Override
Wonsik Kim969167d2014-06-24 16:33:17 +09001014 public void registerCallback(final ITvInputManagerCallback callback, int userId) {
Jae Seo39570912014-02-20 18:23:25 -08001015 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1016 Binder.getCallingUid(), userId, "registerCallback");
1017 final long identity = Binder.clearCallingIdentity();
1018 try {
1019 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001020 final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Kyeongkab.Nam36932052019-03-08 17:53:43 +09001021 if (!userState.mCallbacks.register(callback)) {
1022 Slog.e(TAG, "client process has already died");
Jae Seofea8dd42014-08-26 13:57:41 -07001023 }
Jae Seo39570912014-02-20 18:23:25 -08001024 }
1025 } finally {
1026 Binder.restoreCallingIdentity(identity);
1027 }
1028 }
1029
1030 @Override
Wonsik Kim969167d2014-06-24 16:33:17 +09001031 public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
Jae Seo39570912014-02-20 18:23:25 -08001032 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1033 Binder.getCallingUid(), userId, "unregisterCallback");
1034 final long identity = Binder.clearCallingIdentity();
1035 try {
1036 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001037 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Kyeongkab.Nam36932052019-03-08 17:53:43 +09001038 userState.mCallbacks.unregister(callback);
Jae Seo39570912014-02-20 18:23:25 -08001039 }
1040 } finally {
1041 Binder.restoreCallingIdentity(identity);
1042 }
1043 }
1044
1045 @Override
Jae Seo783645e2014-07-28 17:30:50 +09001046 public boolean isParentalControlsEnabled(int userId) {
1047 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1048 Binder.getCallingUid(), userId, "isParentalControlsEnabled");
1049 final long identity = Binder.clearCallingIdentity();
1050 try {
1051 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001052 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001053 return userState.persistentDataStore.isParentalControlsEnabled();
1054 }
1055 } finally {
1056 Binder.restoreCallingIdentity(identity);
1057 }
1058 }
1059
1060 @Override
1061 public void setParentalControlsEnabled(boolean enabled, int userId) {
1062 ensureParentalControlsPermission();
1063 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1064 Binder.getCallingUid(), userId, "setParentalControlsEnabled");
1065 final long identity = Binder.clearCallingIdentity();
1066 try {
1067 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001068 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001069 userState.persistentDataStore.setParentalControlsEnabled(enabled);
1070 }
1071 } finally {
1072 Binder.restoreCallingIdentity(identity);
1073 }
1074 }
1075
1076 @Override
1077 public boolean isRatingBlocked(String rating, int userId) {
1078 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1079 Binder.getCallingUid(), userId, "isRatingBlocked");
1080 final long identity = Binder.clearCallingIdentity();
1081 try {
1082 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001083 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001084 return userState.persistentDataStore.isRatingBlocked(
1085 TvContentRating.unflattenFromString(rating));
1086 }
1087 } finally {
1088 Binder.restoreCallingIdentity(identity);
1089 }
1090 }
1091
1092 @Override
1093 public List<String> getBlockedRatings(int userId) {
1094 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1095 Binder.getCallingUid(), userId, "getBlockedRatings");
1096 final long identity = Binder.clearCallingIdentity();
1097 try {
1098 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001099 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001100 List<String> ratings = new ArrayList<>();
Jae Seo783645e2014-07-28 17:30:50 +09001101 for (TvContentRating rating
1102 : userState.persistentDataStore.getBlockedRatings()) {
1103 ratings.add(rating.flattenToString());
1104 }
1105 return ratings;
1106 }
1107 } finally {
1108 Binder.restoreCallingIdentity(identity);
1109 }
1110 }
1111
1112 @Override
1113 public void addBlockedRating(String rating, int userId) {
1114 ensureParentalControlsPermission();
1115 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1116 Binder.getCallingUid(), userId, "addBlockedRating");
1117 final long identity = Binder.clearCallingIdentity();
1118 try {
1119 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001120 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001121 userState.persistentDataStore.addBlockedRating(
1122 TvContentRating.unflattenFromString(rating));
1123 }
1124 } finally {
1125 Binder.restoreCallingIdentity(identity);
1126 }
1127 }
1128
1129 @Override
1130 public void removeBlockedRating(String rating, int userId) {
1131 ensureParentalControlsPermission();
1132 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1133 Binder.getCallingUid(), userId, "removeBlockedRating");
1134 final long identity = Binder.clearCallingIdentity();
1135 try {
1136 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001137 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001138 userState.persistentDataStore.removeBlockedRating(
1139 TvContentRating.unflattenFromString(rating));
1140 }
1141 } finally {
1142 Binder.restoreCallingIdentity(identity);
1143 }
1144 }
1145
1146 private void ensureParentalControlsPermission() {
Jae Seofc836f62014-08-27 00:47:56 +00001147 if (mContext.checkCallingPermission(
1148 android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
1149 != PackageManager.PERMISSION_GRANTED) {
1150 throw new SecurityException(
1151 "The caller does not have parental controls permission");
1152 }
Jae Seo783645e2014-07-28 17:30:50 +09001153 }
1154
1155 @Override
Sungsoo Limd6672b52014-04-30 10:43:26 +09001156 public void createSession(final ITvInputClient client, final String inputId,
Jae Seoa826d012016-01-18 13:03:35 -08001157 boolean isRecordingSession, int seq, int userId) {
Jae Seo39570912014-02-20 18:23:25 -08001158 final int callingUid = Binder.getCallingUid();
1159 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1160 userId, "createSession");
1161 final long identity = Binder.clearCallingIdentity();
1162 try {
1163 synchronized (mLock) {
shubang8049f202016-04-25 11:21:42 -07001164 if (userId != mCurrentUserId && !isRecordingSession) {
1165 // A non-recording session of a backgroud (non-current) user
1166 // should not be created.
1167 // Let the client get onConnectionFailed callback for this case.
1168 sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1169 return;
1170 }
Jae Seo4f1a6d42015-07-20 16:15:01 -07001171 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Dongwon Kang426c9a42014-08-26 17:39:21 +09001172 TvInputState inputState = userState.inputMap.get(inputId);
1173 if (inputState == null) {
1174 Slog.w(TAG, "Failed to find input state for inputId=" + inputId);
1175 sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1176 return;
1177 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001178 TvInputInfo info = inputState.info;
Wonsik Kim187423c2014-06-25 14:12:48 +09001179 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
Jae Seo39570912014-02-20 18:23:25 -08001180 if (serviceState == null) {
Wonsik Kim187423c2014-06-25 14:12:48 +09001181 serviceState = new ServiceState(info.getComponent(), resolvedUserId);
1182 userState.serviceStateMap.put(info.getComponent(), serviceState);
Jae Seo39570912014-02-20 18:23:25 -08001183 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +09001184 // Send a null token immediately while reconnecting.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001185 if (serviceState.reconnecting) {
Jae Seo5c80ad22014-06-12 19:52:58 -07001186 sendSessionTokenToClientLocked(client, inputId, null, null, seq);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09001187 return;
1188 }
1189
1190 // Create a new session token and a session state.
1191 IBinder sessionToken = new Binder();
Jae Seo2cdb05e2016-02-04 22:17:13 +09001192 SessionState sessionState = new SessionState(sessionToken, info.getId(),
1193 info.getComponent(), isRecordingSession, client, seq, callingUid,
1194 resolvedUserId);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09001195
1196 // Add them to the global session state map of the current user.
1197 userState.sessionStateMap.put(sessionToken, sessionState);
1198
1199 // Also, add them to the session state map of the current service.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001200 serviceState.sessionTokens.add(sessionToken);
Jae Seo39570912014-02-20 18:23:25 -08001201
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001202 if (serviceState.service != null) {
shubangaf2fb662018-11-09 17:03:46 -08001203 if (!createSessionInternalLocked(serviceState.service, sessionToken,
1204 resolvedUserId)) {
1205 removeSessionStateLocked(sessionToken, resolvedUserId);
1206 }
Jae Seo39570912014-02-20 18:23:25 -08001207 } else {
Wonsik Kim187423c2014-06-25 14:12:48 +09001208 updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
Jae Seo39570912014-02-20 18:23:25 -08001209 }
1210 }
1211 } finally {
1212 Binder.restoreCallingIdentity(identity);
1213 }
1214 }
1215
1216 @Override
1217 public void releaseSession(IBinder sessionToken, int userId) {
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001218 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -07001219 Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001220 }
Jae Seo39570912014-02-20 18:23:25 -08001221 final int callingUid = Binder.getCallingUid();
1222 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1223 userId, "releaseSession");
1224 final long identity = Binder.clearCallingIdentity();
1225 try {
1226 synchronized (mLock) {
Sungsoo Lim2b35a722014-04-17 17:09:15 +09001227 releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
Jae Seo39570912014-02-20 18:23:25 -08001228 }
1229 } finally {
1230 Binder.restoreCallingIdentity(identity);
1231 }
1232 }
1233
1234 @Override
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001235 public void setMainSession(IBinder sessionToken, int userId) {
Shubange41b76f2017-06-07 13:23:12 -07001236 if (mContext.checkCallingPermission(
1237 android.Manifest.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE)
1238 != PackageManager.PERMISSION_GRANTED) {
1239 throw new SecurityException(
1240 "The caller does not have CHANGE_HDMI_CEC_ACTIVE_SOURCE permission");
1241 }
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001242 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -07001243 Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")");
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001244 }
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001245 final int callingUid = Binder.getCallingUid();
1246 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1247 userId, "setMainSession");
1248 final long identity = Binder.clearCallingIdentity();
1249 try {
1250 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001251 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Ji-Hwan Lee956afc22014-07-26 11:31:39 +09001252 if (userState.mainSessionToken == sessionToken) {
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001253 return;
1254 }
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001255 if (DEBUG) {
1256 Slog.d(TAG, "mainSessionToken=" + sessionToken);
Ji-Hwan Lee956afc22014-07-26 11:31:39 +09001257 }
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001258 IBinder oldMainSessionToken = userState.mainSessionToken;
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001259 userState.mainSessionToken = sessionToken;
1260
Ji-Hwan Lee956afc22014-07-26 11:31:39 +09001261 // Inform the new main session first.
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001262 // See {@link TvInputService.Session#onSetMain}.
1263 if (sessionToken != null) {
1264 setMainLocked(sessionToken, true, callingUid, userId);
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001265 }
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001266 if (oldMainSessionToken != null) {
1267 setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId);
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001268 }
1269 }
1270 } finally {
1271 Binder.restoreCallingIdentity(identity);
1272 }
1273 }
1274
1275 @Override
Jae Seo39570912014-02-20 18:23:25 -08001276 public void setSurface(IBinder sessionToken, Surface surface, int userId) {
1277 final int callingUid = Binder.getCallingUid();
1278 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1279 userId, "setSurface");
1280 final long identity = Binder.clearCallingIdentity();
1281 try {
1282 synchronized (mLock) {
1283 try {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001284 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1285 resolvedUserId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001286 if (sessionState.hardwareSessionToken == null) {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001287 getSessionLocked(sessionState).setSurface(surface);
1288 } else {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001289 getSessionLocked(sessionState.hardwareSessionToken,
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001290 Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
1291 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001292 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001293 Slog.e(TAG, "error in setSurface", e);
Jae Seo39570912014-02-20 18:23:25 -08001294 }
1295 }
1296 } finally {
Youngsang Chof8362062014-04-30 17:24:20 +09001297 if (surface != null) {
1298 // surface is not used in TvInputManagerService.
1299 surface.release();
1300 }
Jae Seo39570912014-02-20 18:23:25 -08001301 Binder.restoreCallingIdentity(identity);
1302 }
1303 }
1304
1305 @Override
Youngsang Choe821d712014-07-16 14:22:19 -07001306 public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
1307 int height, int userId) {
1308 final int callingUid = Binder.getCallingUid();
1309 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1310 userId, "dispatchSurfaceChanged");
1311 final long identity = Binder.clearCallingIdentity();
1312 try {
1313 synchronized (mLock) {
1314 try {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001315 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1316 resolvedUserId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001317 getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
1318 height);
1319 if (sessionState.hardwareSessionToken != null) {
1320 getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001321 resolvedUserId).dispatchSurfaceChanged(format, width, height);
1322 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001323 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Choe821d712014-07-16 14:22:19 -07001324 Slog.e(TAG, "error in dispatchSurfaceChanged", e);
1325 }
1326 }
1327 } finally {
1328 Binder.restoreCallingIdentity(identity);
1329 }
1330 }
1331
1332 @Override
Jae Seo39570912014-02-20 18:23:25 -08001333 public void setVolume(IBinder sessionToken, float volume, int userId) {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001334 final float REMOTE_VOLUME_ON = 1.0f;
1335 final float REMOTE_VOLUME_OFF = 0f;
Jae Seo39570912014-02-20 18:23:25 -08001336 final int callingUid = Binder.getCallingUid();
1337 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1338 userId, "setVolume");
1339 final long identity = Binder.clearCallingIdentity();
1340 try {
1341 synchronized (mLock) {
1342 try {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001343 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1344 resolvedUserId);
1345 getSessionLocked(sessionState).setVolume(volume);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001346 if (sessionState.hardwareSessionToken != null) {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001347 // Here, we let the hardware session know only whether volume is on or
1348 // off to prevent that the volume is controlled in the both side.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001349 getSessionLocked(sessionState.hardwareSessionToken,
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001350 Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
1351 ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
1352 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001353 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001354 Slog.e(TAG, "error in setVolume", e);
Jae Seo39570912014-02-20 18:23:25 -08001355 }
1356 }
1357 } finally {
1358 Binder.restoreCallingIdentity(identity);
1359 }
1360 }
1361
1362 @Override
Sungsoo Lim1a6b25e2014-07-09 10:40:43 +09001363 public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
Jae Seo39570912014-02-20 18:23:25 -08001364 final int callingUid = Binder.getCallingUid();
1365 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1366 userId, "tune");
1367 final long identity = Binder.clearCallingIdentity();
1368 try {
1369 synchronized (mLock) {
Jae Seo39570912014-02-20 18:23:25 -08001370 try {
Sungsoo Lim1a6b25e2014-07-09 10:40:43 +09001371 getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
1372 channelUri, params);
Jae Seoc22d0c02014-08-15 13:03:21 -07001373 if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
Youngsang Cho008f6d42014-07-22 21:29:47 -07001374 // Do not log the watch history for passthrough inputs.
1375 return;
1376 }
Jae Seo31dc634be2014-04-15 17:40:23 -07001377
Jae Seo4f1a6d42015-07-20 16:15:01 -07001378 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo31dc634be2014-04-15 17:40:23 -07001379 SessionState sessionState = userState.sessionStateMap.get(sessionToken);
Jae Seoe3c11e82016-02-08 23:18:49 -08001380 if (sessionState.isRecordingSession) {
1381 return;
1382 }
Jae Seo31dc634be2014-04-15 17:40:23 -07001383
Jae Seo7eb75df2014-08-08 22:20:48 -07001384 // Log the start of watch.
Jae Seo31dc634be2014-04-15 17:40:23 -07001385 SomeArgs args = SomeArgs.obtain();
Jae Seo2cdb05e2016-02-04 22:17:13 +09001386 args.arg1 = sessionState.componentName.getPackageName();
Jae Seo7eb75df2014-08-08 22:20:48 -07001387 args.arg2 = System.currentTimeMillis();
1388 args.arg3 = ContentUris.parseId(channelUri);
1389 args.arg4 = params;
1390 args.arg5 = sessionToken;
1391 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args)
1392 .sendToTarget();
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001393 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001394 Slog.e(TAG, "error in tune", e);
Jae Seo39570912014-02-20 18:23:25 -08001395 }
1396 }
1397 } finally {
1398 Binder.restoreCallingIdentity(identity);
1399 }
1400 }
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001401
1402 @Override
Jae Seoa9033832015-03-11 19:29:46 -07001403 public void unblockContent(
Sungsoo Lim9bf671f2014-07-19 12:59:51 +09001404 IBinder sessionToken, String unblockedRating, int userId) {
Dongwon Kange12d8102016-03-04 16:45:39 -08001405 ensureParentalControlsPermission();
Jaewan Kim903d6b72014-07-16 11:28:56 +09001406 final int callingUid = Binder.getCallingUid();
1407 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1408 userId, "unblockContent");
1409 final long identity = Binder.clearCallingIdentity();
1410 try {
1411 synchronized (mLock) {
1412 try {
1413 getSessionLocked(sessionToken, callingUid, resolvedUserId)
Jae Seoa9033832015-03-11 19:29:46 -07001414 .unblockContent(unblockedRating);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001415 } catch (RemoteException | SessionNotFoundException e) {
Jae Seoa9033832015-03-11 19:29:46 -07001416 Slog.e(TAG, "error in unblockContent", e);
Jaewan Kim903d6b72014-07-16 11:28:56 +09001417 }
1418 }
1419 } finally {
1420 Binder.restoreCallingIdentity(identity);
1421 }
1422 }
1423
1424 @Override
Jae Seo2c1c31c2014-07-10 14:57:01 -07001425 public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
1426 final int callingUid = Binder.getCallingUid();
1427 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1428 userId, "setCaptionEnabled");
1429 final long identity = Binder.clearCallingIdentity();
1430 try {
1431 synchronized (mLock) {
1432 try {
1433 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1434 .setCaptionEnabled(enabled);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001435 } catch (RemoteException | SessionNotFoundException e) {
Jae Seo2c1c31c2014-07-10 14:57:01 -07001436 Slog.e(TAG, "error in setCaptionEnabled", e);
1437 }
1438 }
1439 } finally {
1440 Binder.restoreCallingIdentity(identity);
1441 }
1442 }
1443
1444 @Override
Jae Seo10d285a2014-07-31 22:46:47 +09001445 public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
Dongwon Kang1f213912014-07-02 18:35:08 +09001446 final int callingUid = Binder.getCallingUid();
1447 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1448 userId, "selectTrack");
1449 final long identity = Binder.clearCallingIdentity();
1450 try {
1451 synchronized (mLock) {
1452 try {
1453 getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
Jae Seo10d285a2014-07-31 22:46:47 +09001454 type, trackId);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001455 } catch (RemoteException | SessionNotFoundException e) {
Dongwon Kang1f213912014-07-02 18:35:08 +09001456 Slog.e(TAG, "error in selectTrack", e);
1457 }
1458 }
1459 } finally {
1460 Binder.restoreCallingIdentity(identity);
1461 }
1462 }
1463
1464 @Override
Jae Seoa759b112014-07-18 22:16:08 -07001465 public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
1466 int userId) {
1467 final int callingUid = Binder.getCallingUid();
1468 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1469 userId, "sendAppPrivateCommand");
1470 final long identity = Binder.clearCallingIdentity();
1471 try {
1472 synchronized (mLock) {
1473 try {
1474 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1475 .appPrivateCommand(command, data);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001476 } catch (RemoteException | SessionNotFoundException e) {
Jae Seofea8dd42014-08-26 13:57:41 -07001477 Slog.e(TAG, "error in appPrivateCommand", e);
Jae Seoa759b112014-07-18 22:16:08 -07001478 }
1479 }
1480 } finally {
1481 Binder.restoreCallingIdentity(identity);
1482 }
1483 }
1484
1485 @Override
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001486 public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
1487 int userId) {
1488 final int callingUid = Binder.getCallingUid();
1489 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1490 userId, "createOverlayView");
1491 final long identity = Binder.clearCallingIdentity();
1492 try {
1493 synchronized (mLock) {
1494 try {
1495 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1496 .createOverlayView(windowToken, frame);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001497 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001498 Slog.e(TAG, "error in createOverlayView", e);
1499 }
1500 }
1501 } finally {
1502 Binder.restoreCallingIdentity(identity);
1503 }
1504 }
1505
1506 @Override
1507 public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
1508 final int callingUid = Binder.getCallingUid();
1509 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1510 userId, "relayoutOverlayView");
1511 final long identity = Binder.clearCallingIdentity();
1512 try {
1513 synchronized (mLock) {
1514 try {
1515 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1516 .relayoutOverlayView(frame);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001517 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001518 Slog.e(TAG, "error in relayoutOverlayView", e);
1519 }
1520 }
1521 } finally {
1522 Binder.restoreCallingIdentity(identity);
1523 }
1524 }
1525
1526 @Override
1527 public void removeOverlayView(IBinder sessionToken, int userId) {
1528 final int callingUid = Binder.getCallingUid();
1529 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1530 userId, "removeOverlayView");
1531 final long identity = Binder.clearCallingIdentity();
1532 try {
1533 synchronized (mLock) {
1534 try {
1535 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1536 .removeOverlayView();
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001537 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001538 Slog.e(TAG, "error in removeOverlayView", e);
1539 }
1540 }
1541 } finally {
1542 Binder.restoreCallingIdentity(identity);
1543 }
1544 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001545
1546 @Override
Jae Seoa826d012016-01-18 13:03:35 -08001547 public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
1548 final int callingUid = Binder.getCallingUid();
1549 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1550 userId, "timeShiftPlay");
1551 final long identity = Binder.clearCallingIdentity();
1552 try {
1553 synchronized (mLock) {
1554 try {
1555 getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay(
1556 recordedProgramUri);
1557 } catch (RemoteException | SessionNotFoundException e) {
1558 Slog.e(TAG, "error in timeShiftPlay", e);
1559 }
1560 }
1561 } finally {
1562 Binder.restoreCallingIdentity(identity);
1563 }
1564 }
1565
1566 @Override
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001567 public void timeShiftPause(IBinder sessionToken, int userId) {
1568 final int callingUid = Binder.getCallingUid();
1569 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1570 userId, "timeShiftPause");
1571 final long identity = Binder.clearCallingIdentity();
1572 try {
1573 synchronized (mLock) {
1574 try {
Jae Seoa826d012016-01-18 13:03:35 -08001575 getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause();
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001576 } catch (RemoteException | SessionNotFoundException e) {
1577 Slog.e(TAG, "error in timeShiftPause", e);
1578 }
1579 }
1580 } finally {
1581 Binder.restoreCallingIdentity(identity);
1582 }
1583 }
1584
1585 @Override
1586 public void timeShiftResume(IBinder sessionToken, int userId) {
1587 final int callingUid = Binder.getCallingUid();
1588 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1589 userId, "timeShiftResume");
1590 final long identity = Binder.clearCallingIdentity();
1591 try {
1592 synchronized (mLock) {
1593 try {
1594 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1595 .timeShiftResume();
1596 } catch (RemoteException | SessionNotFoundException e) {
1597 Slog.e(TAG, "error in timeShiftResume", e);
1598 }
1599 }
1600 } finally {
1601 Binder.restoreCallingIdentity(identity);
1602 }
1603 }
1604
1605 @Override
1606 public void timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId) {
1607 final int callingUid = Binder.getCallingUid();
1608 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1609 userId, "timeShiftSeekTo");
1610 final long identity = Binder.clearCallingIdentity();
1611 try {
1612 synchronized (mLock) {
1613 try {
1614 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1615 .timeShiftSeekTo(timeMs);
1616 } catch (RemoteException | SessionNotFoundException e) {
1617 Slog.e(TAG, "error in timeShiftSeekTo", e);
1618 }
1619 }
1620 } finally {
1621 Binder.restoreCallingIdentity(identity);
1622 }
1623 }
1624
1625 @Override
Jae Seo4b34cc72015-05-15 17:29:39 -07001626 public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params,
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001627 int userId) {
1628 final int callingUid = Binder.getCallingUid();
1629 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
Jae Seo4b34cc72015-05-15 17:29:39 -07001630 userId, "timeShiftSetPlaybackParams");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001631 final long identity = Binder.clearCallingIdentity();
1632 try {
1633 synchronized (mLock) {
1634 try {
1635 getSessionLocked(sessionToken, callingUid, resolvedUserId)
Jae Seo4b34cc72015-05-15 17:29:39 -07001636 .timeShiftSetPlaybackParams(params);
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001637 } catch (RemoteException | SessionNotFoundException e) {
Jae Seo4b34cc72015-05-15 17:29:39 -07001638 Slog.e(TAG, "error in timeShiftSetPlaybackParams", e);
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001639 }
1640 }
1641 } finally {
1642 Binder.restoreCallingIdentity(identity);
1643 }
1644 }
1645
1646 @Override
Jae Seo465f0d62015-04-06 18:40:46 -07001647 public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable,
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001648 int userId) {
1649 final int callingUid = Binder.getCallingUid();
1650 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
Jae Seo465f0d62015-04-06 18:40:46 -07001651 userId, "timeShiftEnablePositionTracking");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001652 final long identity = Binder.clearCallingIdentity();
1653 try {
1654 synchronized (mLock) {
1655 try {
1656 getSessionLocked(sessionToken, callingUid, resolvedUserId)
Jae Seo465f0d62015-04-06 18:40:46 -07001657 .timeShiftEnablePositionTracking(enable);
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001658 } catch (RemoteException | SessionNotFoundException e) {
Jae Seo465f0d62015-04-06 18:40:46 -07001659 Slog.e(TAG, "error in timeShiftEnablePositionTracking", e);
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001660 }
1661 }
1662 } finally {
1663 Binder.restoreCallingIdentity(identity);
1664 }
1665 }
1666
1667 @Override
Dongwon Kang0cb52442016-04-22 11:00:03 -07001668 public void startRecording(IBinder sessionToken, @Nullable Uri programUri, int userId) {
Jae Seoa826d012016-01-18 13:03:35 -08001669 final int callingUid = Binder.getCallingUid();
1670 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1671 userId, "startRecording");
1672 final long identity = Binder.clearCallingIdentity();
1673 try {
1674 synchronized (mLock) {
1675 try {
Jae Seo4eee6a72016-02-06 11:11:35 +09001676 getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording(
Dongwon Kang0cb52442016-04-22 11:00:03 -07001677 programUri);
Jae Seoa826d012016-01-18 13:03:35 -08001678 } catch (RemoteException | SessionNotFoundException e) {
1679 Slog.e(TAG, "error in startRecording", e);
1680 }
1681 }
1682 } finally {
1683 Binder.restoreCallingIdentity(identity);
1684 }
1685 }
1686
1687 @Override
1688 public void stopRecording(IBinder sessionToken, int userId) {
1689 final int callingUid = Binder.getCallingUid();
1690 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1691 userId, "stopRecording");
1692 final long identity = Binder.clearCallingIdentity();
1693 try {
1694 synchronized (mLock) {
1695 try {
1696 getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording();
1697 } catch (RemoteException | SessionNotFoundException e) {
1698 Slog.e(TAG, "error in stopRecording", e);
1699 }
1700 }
1701 } finally {
1702 Binder.restoreCallingIdentity(identity);
1703 }
1704 }
1705
1706 @Override
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001707 public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
Wonsik Kim969167d2014-06-24 16:33:17 +09001708 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001709 != PackageManager.PERMISSION_GRANTED) {
1710 return null;
1711 }
1712
1713 final long identity = Binder.clearCallingIdentity();
1714 try {
1715 return mTvInputHardwareManager.getHardwareList();
1716 } finally {
1717 Binder.restoreCallingIdentity(identity);
1718 }
1719 }
1720
1721 @Override
1722 public ITvInputHardware acquireTvInputHardware(int deviceId,
Wonsik Kim969167d2014-06-24 16:33:17 +09001723 ITvInputHardwareCallback callback, TvInputInfo info, int userId)
1724 throws RemoteException {
1725 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001726 != PackageManager.PERMISSION_GRANTED) {
1727 return null;
1728 }
1729
1730 final long identity = Binder.clearCallingIdentity();
1731 final int callingUid = Binder.getCallingUid();
1732 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1733 userId, "acquireTvInputHardware");
1734 try {
1735 return mTvInputHardwareManager.acquireHardware(
Wonsik Kim969167d2014-06-24 16:33:17 +09001736 deviceId, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001737 } finally {
1738 Binder.restoreCallingIdentity(identity);
1739 }
1740 }
1741
1742 @Override
1743 public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
1744 throws RemoteException {
Wonsik Kim969167d2014-06-24 16:33:17 +09001745 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001746 != PackageManager.PERMISSION_GRANTED) {
1747 return;
1748 }
1749
1750 final long identity = Binder.clearCallingIdentity();
1751 final int callingUid = Binder.getCallingUid();
1752 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1753 userId, "releaseTvInputHardware");
1754 try {
1755 mTvInputHardwareManager.releaseHardware(
1756 deviceId, hardware, callingUid, resolvedUserId);
1757 } finally {
1758 Binder.restoreCallingIdentity(identity);
1759 }
1760 }
Jaewan Kime14c3f42014-06-27 13:47:48 +09001761
1762 @Override
Jaesung Chung58739e72015-04-24 19:39:59 +09001763 public List<DvbDeviceInfo> getDvbDeviceList() throws RemoteException {
1764 if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
1765 != PackageManager.PERMISSION_GRANTED) {
1766 throw new SecurityException("Requires DVB_DEVICE permission");
1767 }
1768
1769 final long identity = Binder.clearCallingIdentity();
1770 try {
Jiabinef1659f2016-11-18 10:50:15 +09001771 // Pattern1: /dev/dvb%d.frontend%d
1772 ArrayList<DvbDeviceInfo> deviceInfosFromPattern1 = new ArrayList<>();
Jaesung Chung58739e72015-04-24 19:39:59 +09001773 File devDirectory = new File("/dev");
Jiabinef1659f2016-11-18 10:50:15 +09001774 boolean dvbDirectoryFound = false;
Jaesung Chung58739e72015-04-24 19:39:59 +09001775 for (String fileName : devDirectory.list()) {
1776 Matcher matcher = sFrontEndDevicePattern.matcher(fileName);
1777 if (matcher.find()) {
1778 int adapterId = Integer.parseInt(matcher.group(1));
1779 int deviceId = Integer.parseInt(matcher.group(2));
Jiabinef1659f2016-11-18 10:50:15 +09001780 deviceInfosFromPattern1.add(new DvbDeviceInfo(adapterId, deviceId));
1781 }
1782 if (TextUtils.equals("dvb", fileName)) {
1783 dvbDirectoryFound = true;
Jaesung Chung58739e72015-04-24 19:39:59 +09001784 }
1785 }
Jiabinef1659f2016-11-18 10:50:15 +09001786 if (!dvbDirectoryFound) {
1787 return Collections.unmodifiableList(deviceInfosFromPattern1);
1788 }
1789 File dvbDirectory = new File(DVB_DIRECTORY);
1790 // Pattern2: /dev/dvb/adapter%d/frontend%d
1791 ArrayList<DvbDeviceInfo> deviceInfosFromPattern2 = new ArrayList<>();
1792 for (String fileNameInDvb : dvbDirectory.list()) {
1793 Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
1794 if (adapterMatcher.find()) {
1795 int adapterId = Integer.parseInt(adapterMatcher.group(1));
1796 File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
1797 for (String fileNameInAdapter : adapterDirectory.list()) {
1798 Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
1799 fileNameInAdapter);
1800 if (frontendMatcher.find()) {
1801 int deviceId = Integer.parseInt(frontendMatcher.group(1));
1802 deviceInfosFromPattern2.add(
1803 new DvbDeviceInfo(adapterId, deviceId));
1804 }
1805 }
1806 }
1807 }
1808 return deviceInfosFromPattern2.isEmpty()
1809 ? Collections.unmodifiableList(deviceInfosFromPattern1)
1810 : Collections.unmodifiableList(deviceInfosFromPattern2);
Jaesung Chung58739e72015-04-24 19:39:59 +09001811 } finally {
1812 Binder.restoreCallingIdentity(identity);
1813 }
1814 }
1815
1816 @Override
1817 public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device)
1818 throws RemoteException {
1819 if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
1820 != PackageManager.PERMISSION_GRANTED) {
1821 throw new SecurityException("Requires DVB_DEVICE permission");
1822 }
1823
Jiabinef1659f2016-11-18 10:50:15 +09001824 File devDirectory = new File("/dev");
1825 boolean dvbDeviceFound = false;
1826 for (String fileName : devDirectory.list()) {
1827 if (TextUtils.equals("dvb", fileName)) {
1828 File dvbDirectory = new File(DVB_DIRECTORY);
1829 for (String fileNameInDvb : dvbDirectory.list()) {
1830 Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
1831 if (adapterMatcher.find()) {
1832 File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
1833 for (String fileNameInAdapter : adapterDirectory.list()) {
1834 Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
1835 fileNameInAdapter);
1836 if (frontendMatcher.find()) {
1837 dvbDeviceFound = true;
1838 break;
1839 }
1840 }
1841 }
1842 if (dvbDeviceFound) {
1843 break;
1844 }
1845 }
1846 }
1847 if (dvbDeviceFound) {
1848 break;
1849 }
1850 }
1851
Jaesung Chung58739e72015-04-24 19:39:59 +09001852 final long identity = Binder.clearCallingIdentity();
1853 try {
1854 String deviceFileName;
1855 switch (device) {
1856 case TvInputManager.DVB_DEVICE_DEMUX:
Jiabinef1659f2016-11-18 10:50:15 +09001857 deviceFileName = String.format(dvbDeviceFound
1858 ? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d",
1859 info.getAdapterId(), info.getDeviceId());
Jaesung Chung58739e72015-04-24 19:39:59 +09001860 break;
1861 case TvInputManager.DVB_DEVICE_DVR:
Jiabinef1659f2016-11-18 10:50:15 +09001862 deviceFileName = String.format(dvbDeviceFound
1863 ? "/dev/dvb/adapter%d/dvr%d" : "/dev/dvb%d.dvr%d",
1864 info.getAdapterId(), info.getDeviceId());
Jaesung Chung58739e72015-04-24 19:39:59 +09001865 break;
1866 case TvInputManager.DVB_DEVICE_FRONTEND:
Jiabinef1659f2016-11-18 10:50:15 +09001867 deviceFileName = String.format(dvbDeviceFound
1868 ? "/dev/dvb/adapter%d/frontend%d" : "/dev/dvb%d.frontend%d",
1869 info.getAdapterId(), info.getDeviceId());
Jaesung Chung58739e72015-04-24 19:39:59 +09001870 break;
1871 default:
1872 throw new IllegalArgumentException("Invalid DVB device: " + device);
1873 }
1874 try {
1875 // The DVB frontend device only needs to be opened in read/write mode, which
1876 // allows performing tuning operations. The DVB demux and DVR device are enough
1877 // to be opened in read only mode.
1878 return ParcelFileDescriptor.open(new File(deviceFileName),
1879 TvInputManager.DVB_DEVICE_FRONTEND == device
1880 ? ParcelFileDescriptor.MODE_READ_WRITE
1881 : ParcelFileDescriptor.MODE_READ_ONLY);
1882 } catch (FileNotFoundException e) {
1883 return null;
1884 }
1885 } finally {
1886 Binder.restoreCallingIdentity(identity);
1887 }
1888 }
1889
1890 @Override
Terry Heoc086a3d2014-06-18 14:26:44 +09001891 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
1892 throws RemoteException {
Shubangdd3ec0b2017-06-23 13:54:10 -07001893 ensureCaptureTvInputPermission();
Terry Heoc086a3d2014-06-18 14:26:44 +09001894
1895 final long identity = Binder.clearCallingIdentity();
1896 final int callingUid = Binder.getCallingUid();
1897 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1898 userId, "getAvailableTvStreamConfigList");
1899 try {
1900 return mTvInputHardwareManager.getAvailableTvStreamConfigList(
1901 inputId, callingUid, resolvedUserId);
1902 } finally {
1903 Binder.restoreCallingIdentity(identity);
1904 }
1905 }
1906
1907 @Override
1908 public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
1909 int userId)
1910 throws RemoteException {
Shubangdd3ec0b2017-06-23 13:54:10 -07001911 ensureCaptureTvInputPermission();
Terry Heoc086a3d2014-06-18 14:26:44 +09001912
1913 final long identity = Binder.clearCallingIdentity();
1914 final int callingUid = Binder.getCallingUid();
1915 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1916 userId, "captureFrame");
1917 try {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001918 String hardwareInputId = null;
Terry Heo79124a72014-07-21 15:17:17 +09001919 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001920 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001921 if (userState.inputMap.get(inputId) == null) {
Jae Seofea8dd42014-08-26 13:57:41 -07001922 Slog.e(TAG, "input not found for " + inputId);
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001923 return false;
1924 }
1925 for (SessionState sessionState : userState.sessionStateMap.values()) {
Jae Seo2cdb05e2016-02-04 22:17:13 +09001926 if (sessionState.inputId.equals(inputId)
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001927 && sessionState.hardwareSessionToken != null) {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001928 hardwareInputId = userState.sessionStateMap.get(
Jae Seo2cdb05e2016-02-04 22:17:13 +09001929 sessionState.hardwareSessionToken).inputId;
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001930 break;
1931 }
1932 }
Terry Heo79124a72014-07-21 15:17:17 +09001933 }
Terry Heoc086a3d2014-06-18 14:26:44 +09001934 return mTvInputHardwareManager.captureFrame(
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001935 (hardwareInputId != null) ? hardwareInputId : inputId,
Terry Heo79124a72014-07-21 15:17:17 +09001936 surface, config, callingUid, resolvedUserId);
Terry Heoc086a3d2014-06-18 14:26:44 +09001937 } finally {
1938 Binder.restoreCallingIdentity(identity);
1939 }
1940 }
1941
1942 @Override
Terry Heodf9f0a32014-08-06 13:53:33 +09001943 public boolean isSingleSessionActive(int userId) throws RemoteException {
Shubangdd3ec0b2017-06-23 13:54:10 -07001944 ensureCaptureTvInputPermission();
Terry Heodf9f0a32014-08-06 13:53:33 +09001945 final long identity = Binder.clearCallingIdentity();
1946 final int callingUid = Binder.getCallingUid();
1947 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1948 userId, "isSingleSessionActive");
1949 try {
1950 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001951 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Terry Heodf9f0a32014-08-06 13:53:33 +09001952 if (userState.sessionStateMap.size() == 1) {
1953 return true;
Jae Seo93ff14b2015-06-21 14:08:54 -07001954 } else if (userState.sessionStateMap.size() == 2) {
Terry Heodf9f0a32014-08-06 13:53:33 +09001955 SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
Jae Seo93ff14b2015-06-21 14:08:54 -07001956 new SessionState[2]);
Terry Heodf9f0a32014-08-06 13:53:33 +09001957 // Check if there is a wrapper input.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001958 if (sessionStates[0].hardwareSessionToken != null
1959 || sessionStates[1].hardwareSessionToken != null) {
Terry Heodf9f0a32014-08-06 13:53:33 +09001960 return true;
1961 }
1962 }
1963 return false;
1964 }
1965 } finally {
1966 Binder.restoreCallingIdentity(identity);
1967 }
1968 }
1969
Shubangdd3ec0b2017-06-23 13:54:10 -07001970 private void ensureCaptureTvInputPermission() {
1971 if (mContext.checkCallingPermission(
1972 android.Manifest.permission.CAPTURE_TV_INPUT)
1973 != PackageManager.PERMISSION_GRANTED) {
1974 throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1975 }
1976 }
1977
Terry Heodf9f0a32014-08-06 13:53:33 +09001978 @Override
Dongwon Kang2e7f5ce2017-04-05 18:33:50 -07001979 public void requestChannelBrowsable(Uri channelUri, int userId)
1980 throws RemoteException {
1981 final String callingPackageName = getCallingPackageName();
1982 final long identity = Binder.clearCallingIdentity();
1983 final int callingUid = Binder.getCallingUid();
1984 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1985 userId, "requestChannelBrowsable");
1986 try {
1987 Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
1988 List<ResolveInfo> list = getContext().getPackageManager()
1989 .queryBroadcastReceivers(intent, 0);
1990 if (list != null) {
1991 for (ResolveInfo info : list) {
1992 String receiverPackageName = info.activityInfo.packageName;
1993 intent.putExtra(TvContract.EXTRA_CHANNEL_ID, ContentUris.parseId(
1994 channelUri));
1995 intent.putExtra(TvContract.EXTRA_PACKAGE_NAME, callingPackageName);
1996 intent.setPackage(receiverPackageName);
1997 getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
1998 }
1999 }
2000 } finally {
2001 Binder.restoreCallingIdentity(identity);
2002 }
2003 }
2004
2005 @Override
Jae Seo0f8fc342014-07-02 10:47:08 -07002006 @SuppressWarnings("resource")
Jaewan Kime14c3f42014-06-27 13:47:48 +09002007 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
2008 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06002009 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jaewan Kime14c3f42014-06-27 13:47:48 +09002010
2011 synchronized (mLock) {
2012 pw.println("User Ids (Current user: " + mCurrentUserId + "):");
2013 pw.increaseIndent();
2014 for (int i = 0; i < mUserStates.size(); i++) {
2015 int userId = mUserStates.keyAt(i);
2016 pw.println(Integer.valueOf(userId));
2017 }
2018 pw.decreaseIndent();
2019
2020 for (int i = 0; i < mUserStates.size(); i++) {
2021 int userId = mUserStates.keyAt(i);
Jae Seo4f1a6d42015-07-20 16:15:01 -07002022 UserState userState = getOrCreateUserStateLocked(userId);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002023 pw.println("UserState (" + userId + "):");
2024 pw.increaseIndent();
2025
Wonsik Kim969167d2014-06-24 16:33:17 +09002026 pw.println("inputMap: inputId -> TvInputState");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002027 pw.increaseIndent();
Jaewan Kim8e6b51b2014-07-15 13:01:57 +09002028 for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) {
2029 pw.println(entry.getKey() + ": " + entry.getValue());
Jaewan Kime14c3f42014-06-27 13:47:48 +09002030 }
2031 pw.decreaseIndent();
2032
Wonsik Kim969167d2014-06-24 16:33:17 +09002033 pw.println("packageSet:");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002034 pw.increaseIndent();
Wonsik Kim969167d2014-06-24 16:33:17 +09002035 for (String packageName : userState.packageSet) {
Jaewan Kime14c3f42014-06-27 13:47:48 +09002036 pw.println(packageName);
2037 }
2038 pw.decreaseIndent();
2039
2040 pw.println("clientStateMap: ITvInputClient -> ClientState");
2041 pw.increaseIndent();
2042 for (Map.Entry<IBinder, ClientState> entry :
2043 userState.clientStateMap.entrySet()) {
2044 ClientState client = entry.getValue();
2045 pw.println(entry.getKey() + ": " + client);
2046
2047 pw.increaseIndent();
2048
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002049 pw.println("sessionTokens:");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002050 pw.increaseIndent();
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002051 for (IBinder token : client.sessionTokens) {
Jaewan Kime14c3f42014-06-27 13:47:48 +09002052 pw.println("" + token);
2053 }
2054 pw.decreaseIndent();
2055
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002056 pw.println("clientTokens: " + client.clientToken);
2057 pw.println("userId: " + client.userId);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002058
2059 pw.decreaseIndent();
2060 }
2061 pw.decreaseIndent();
2062
Wonsik Kim187423c2014-06-25 14:12:48 +09002063 pw.println("serviceStateMap: ComponentName -> ServiceState");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002064 pw.increaseIndent();
Wonsik Kim187423c2014-06-25 14:12:48 +09002065 for (Map.Entry<ComponentName, ServiceState> entry :
Jaewan Kime14c3f42014-06-27 13:47:48 +09002066 userState.serviceStateMap.entrySet()) {
2067 ServiceState service = entry.getValue();
2068 pw.println(entry.getKey() + ": " + service);
2069
2070 pw.increaseIndent();
2071
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002072 pw.println("sessionTokens:");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002073 pw.increaseIndent();
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002074 for (IBinder token : service.sessionTokens) {
Jaewan Kime14c3f42014-06-27 13:47:48 +09002075 pw.println("" + token);
2076 }
2077 pw.decreaseIndent();
2078
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002079 pw.println("service: " + service.service);
2080 pw.println("callback: " + service.callback);
2081 pw.println("bound: " + service.bound);
2082 pw.println("reconnecting: " + service.reconnecting);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002083
2084 pw.decreaseIndent();
2085 }
2086 pw.decreaseIndent();
2087
2088 pw.println("sessionStateMap: ITvInputSession -> SessionState");
2089 pw.increaseIndent();
2090 for (Map.Entry<IBinder, SessionState> entry :
2091 userState.sessionStateMap.entrySet()) {
2092 SessionState session = entry.getValue();
2093 pw.println(entry.getKey() + ": " + session);
2094
2095 pw.increaseIndent();
Jae Seo2cdb05e2016-02-04 22:17:13 +09002096 pw.println("inputId: " + session.inputId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002097 pw.println("client: " + session.client);
2098 pw.println("seq: " + session.seq);
2099 pw.println("callingUid: " + session.callingUid);
2100 pw.println("userId: " + session.userId);
2101 pw.println("sessionToken: " + session.sessionToken);
2102 pw.println("session: " + session.session);
2103 pw.println("logUri: " + session.logUri);
2104 pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002105 pw.decreaseIndent();
2106 }
2107 pw.decreaseIndent();
2108
Kyeongkab.Nam36932052019-03-08 17:53:43 +09002109 pw.println("mCallbacks:");
Wonsik Kim969167d2014-06-24 16:33:17 +09002110 pw.increaseIndent();
Kyeongkab.Nam36932052019-03-08 17:53:43 +09002111 int n = userState.mCallbacks.beginBroadcast();
2112 for (int j = 0; j < n; ++j) {
2113 pw.println(userState.mCallbacks.getRegisteredCallbackItem(j).toString());
Wonsik Kim969167d2014-06-24 16:33:17 +09002114 }
Kyeongkab.Nam36932052019-03-08 17:53:43 +09002115 userState.mCallbacks.finishBroadcast();
Wonsik Kim969167d2014-06-24 16:33:17 +09002116 pw.decreaseIndent();
2117
Ji-Hwan Lee956afc22014-07-26 11:31:39 +09002118 pw.println("mainSessionToken: " + userState.mainSessionToken);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002119 pw.decreaseIndent();
2120 }
2121 }
yangren8d718e12016-02-15 17:02:49 +09002122 mTvInputHardwareManager.dump(fd, writer, args);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002123 }
Jae Seo39570912014-02-20 18:23:25 -08002124 }
2125
Wonsik Kim969167d2014-06-24 16:33:17 +09002126 private static final class UserState {
2127 // A mapping from the TV input id to its TvInputState.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002128 private Map<String, TvInputState> inputMap = new HashMap<>();
Wonsik Kim969167d2014-06-24 16:33:17 +09002129
2130 // A set of all TV input packages.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002131 private final Set<String> packageSet = new HashSet<>();
Jae Seo5c80ad22014-06-12 19:52:58 -07002132
Jae Seo9c165d62014-08-25 14:39:26 -07002133 // A list of all TV content rating systems defined.
2134 private final List<TvContentRatingSystemInfo>
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002135 contentRatingSystemList = new ArrayList<>();
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +09002136
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002137 // A mapping from the token of a client to its state.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002138 private final Map<IBinder, ClientState> clientStateMap = new HashMap<>();
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002139
Jae Seo39570912014-02-20 18:23:25 -08002140 // A mapping from the name of a TV input service to its state.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002141 private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>();
Jae Seo39570912014-02-20 18:23:25 -08002142
2143 // A mapping from the token of a TV input session to its state.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002144 private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>();
Wonsik Kim969167d2014-06-24 16:33:17 +09002145
Kyeongkab.Nam36932052019-03-08 17:53:43 +09002146 // A list of callbacks.
2147 private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks =
2148 new RemoteCallbackList<ITvInputManagerCallback>();
Terry Heo79124a72014-07-21 15:17:17 +09002149
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09002150 // The token of a "main" TV input session.
2151 private IBinder mainSessionToken = null;
Jae Seo783645e2014-07-28 17:30:50 +09002152
2153 // Persistent data store for all internal settings maintained by the TV input manager
2154 // service.
2155 private final PersistentDataStore persistentDataStore;
2156
2157 private UserState(Context context, int userId) {
2158 persistentDataStore = new PersistentDataStore(context, userId);
2159 }
Jae Seo39570912014-02-20 18:23:25 -08002160 }
2161
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002162 private final class ClientState implements IBinder.DeathRecipient {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002163 private final List<IBinder> sessionTokens = new ArrayList<>();
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002164
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002165 private IBinder clientToken;
2166 private final int userId;
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002167
2168 ClientState(IBinder clientToken, int userId) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002169 this.clientToken = clientToken;
2170 this.userId = userId;
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002171 }
2172
2173 public boolean isEmpty() {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002174 return sessionTokens.isEmpty();
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002175 }
2176
2177 @Override
2178 public void binderDied() {
2179 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002180 UserState userState = getOrCreateUserStateLocked(userId);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002181 // DO NOT remove the client state of clientStateMap in this method. It will be
Ji-Hwan Leea65118e2014-07-24 16:30:02 +09002182 // removed in releaseSessionLocked().
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002183 ClientState clientState = userState.clientStateMap.get(clientToken);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002184 if (clientState != null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002185 while (clientState.sessionTokens.size() > 0) {
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002186 releaseSessionLocked(
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002187 clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002188 }
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002189 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002190 clientToken = null;
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002191 }
2192 }
2193 }
2194
Jae Seo39570912014-02-20 18:23:25 -08002195 private final class ServiceState {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002196 private final List<IBinder> sessionTokens = new ArrayList<>();
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002197 private final ServiceConnection connection;
2198 private final ComponentName component;
2199 private final boolean isHardware;
Shubang71d5c762017-02-23 15:46:40 -08002200 private final Map<String, TvInputInfo> hardwareInputMap = new HashMap<>();
Jae Seo39570912014-02-20 18:23:25 -08002201
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002202 private ITvInputService service;
2203 private ServiceCallback callback;
2204 private boolean bound;
2205 private boolean reconnecting;
Jae Seo39570912014-02-20 18:23:25 -08002206
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002207 private ServiceState(ComponentName component, int userId) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002208 this.component = component;
2209 this.connection = new InputServiceConnection(component, userId);
2210 this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
2211 }
2212 }
2213
2214 private static final class TvInputState {
2215 // A TvInputInfo object which represents the TV input.
2216 private TvInputInfo info;
2217
2218 // The state of TV input. Connected by default.
2219 private int state = INPUT_STATE_CONNECTED;
2220
2221 @Override
2222 public String toString() {
2223 return "info: " + info + "; state: " + state;
Jae Seo39570912014-02-20 18:23:25 -08002224 }
2225 }
2226
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002227 private final class SessionState implements IBinder.DeathRecipient {
Jae Seo2cdb05e2016-02-04 22:17:13 +09002228 private final String inputId;
2229 private final ComponentName componentName;
Jae Seoa826d012016-01-18 13:03:35 -08002230 private final boolean isRecordingSession;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002231 private final ITvInputClient client;
2232 private final int seq;
2233 private final int callingUid;
2234 private final int userId;
2235 private final IBinder sessionToken;
2236 private ITvInputSession session;
2237 private Uri logUri;
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09002238 // Not null if this session represents an external device connected to a hardware TV input.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002239 private IBinder hardwareSessionToken;
Jae Seo39570912014-02-20 18:23:25 -08002240
Jae Seo2cdb05e2016-02-04 22:17:13 +09002241 private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
2242 boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
2243 int userId) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002244 this.sessionToken = sessionToken;
Jae Seo2cdb05e2016-02-04 22:17:13 +09002245 this.inputId = inputId;
2246 this.componentName = componentName;
Jae Seoa826d012016-01-18 13:03:35 -08002247 this.isRecordingSession = isRecordingSession;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002248 this.client = client;
2249 this.seq = seq;
2250 this.callingUid = callingUid;
2251 this.userId = userId;
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002252 }
2253
2254 @Override
2255 public void binderDied() {
2256 synchronized (mLock) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002257 session = null;
shubang8049f202016-04-25 11:21:42 -07002258 clearSessionAndNotifyClientLocked(this);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002259 }
Jae Seo39570912014-02-20 18:23:25 -08002260 }
2261 }
2262
2263 private final class InputServiceConnection implements ServiceConnection {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002264 private final ComponentName mComponent;
Jae Seo39570912014-02-20 18:23:25 -08002265 private final int mUserId;
2266
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002267 private InputServiceConnection(ComponentName component, int userId) {
2268 mComponent = component;
Jae Seo39570912014-02-20 18:23:25 -08002269 mUserId = userId;
2270 }
2271
2272 @Override
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002273 public void onServiceConnected(ComponentName component, IBinder service) {
Jae Seo39570912014-02-20 18:23:25 -08002274 if (DEBUG) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002275 Slog.d(TAG, "onServiceConnected(component=" + component + ")");
Jae Seo39570912014-02-20 18:23:25 -08002276 }
2277 synchronized (mLock) {
Dongwon Kang81e3c3e2015-09-11 15:24:25 -07002278 UserState userState = mUserStates.get(mUserId);
2279 if (userState == null) {
2280 // The user was removed while connecting.
2281 mContext.unbindService(this);
2282 return;
2283 }
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002284 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002285 serviceState.service = ITvInputService.Stub.asInterface(service);
Jae Seo39570912014-02-20 18:23:25 -08002286
2287 // Register a callback, if we need to.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002288 if (serviceState.isHardware && serviceState.callback == null) {
2289 serviceState.callback = new ServiceCallback(mComponent, mUserId);
Jae Seo39570912014-02-20 18:23:25 -08002290 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002291 serviceState.service.registerCallback(serviceState.callback);
Jae Seo39570912014-02-20 18:23:25 -08002292 } catch (RemoteException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09002293 Slog.e(TAG, "error in registerCallback", e);
Jae Seo39570912014-02-20 18:23:25 -08002294 }
2295 }
2296
shubangaf2fb662018-11-09 17:03:46 -08002297 List<IBinder> tokensToBeRemoved = new ArrayList<>();
2298
Jae Seo39570912014-02-20 18:23:25 -08002299 // And create sessions, if any.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002300 for (IBinder sessionToken : serviceState.sessionTokens) {
shubangaf2fb662018-11-09 17:03:46 -08002301 if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
2302 tokensToBeRemoved.add(sessionToken);
2303 }
2304 }
2305
2306 for (IBinder sessionToken : tokensToBeRemoved) {
2307 removeSessionStateLocked(sessionToken, mUserId);
Jae Seo39570912014-02-20 18:23:25 -08002308 }
Wonsik Kim969167d2014-06-24 16:33:17 +09002309
Wonsik Kim187423c2014-06-25 14:12:48 +09002310 for (TvInputState inputState : userState.inputMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002311 if (inputState.info.getComponent().equals(component)
Jae Seo82fce642015-04-20 15:37:50 -07002312 && inputState.state != INPUT_STATE_CONNECTED) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002313 notifyInputStateChangedLocked(userState, inputState.info.getId(),
2314 inputState.state, null);
Wonsik Kim187423c2014-06-25 14:12:48 +09002315 }
2316 }
2317
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002318 if (serviceState.isHardware) {
Shubang71d5c762017-02-23 15:46:40 -08002319 serviceState.hardwareInputMap.clear();
Jae Seoc980f43d2016-02-09 23:46:58 -08002320 for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) {
Wonsik Kim187423c2014-06-25 14:12:48 +09002321 try {
Jae Seoc980f43d2016-02-09 23:46:58 -08002322 serviceState.service.notifyHardwareAdded(hardware);
Wonsik Kim187423c2014-06-25 14:12:48 +09002323 } catch (RemoteException e) {
2324 Slog.e(TAG, "error in notifyHardwareAdded", e);
2325 }
2326 }
Jae Seoc980f43d2016-02-09 23:46:58 -08002327 for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002328 try {
Jae Seoc980f43d2016-02-09 23:46:58 -08002329 serviceState.service.notifyHdmiDeviceAdded(device);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002330 } catch (RemoteException e) {
Jae Seo546c6352014-08-07 11:57:01 -07002331 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002332 }
2333 }
Wonsik Kim969167d2014-06-24 16:33:17 +09002334 }
Jae Seo39570912014-02-20 18:23:25 -08002335 }
2336 }
2337
2338 @Override
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002339 public void onServiceDisconnected(ComponentName component) {
Jae Seo39570912014-02-20 18:23:25 -08002340 if (DEBUG) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002341 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")");
Jae Seo39570912014-02-20 18:23:25 -08002342 }
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002343 if (!mComponent.equals(component)) {
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002344 throw new IllegalArgumentException("Mismatched ComponentName: "
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002345 + mComponent + " (expected), " + component + " (actual).");
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002346 }
2347 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002348 UserState userState = getOrCreateUserStateLocked(mUserId);
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002349 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002350 if (serviceState != null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002351 serviceState.reconnecting = true;
2352 serviceState.bound = false;
2353 serviceState.service = null;
2354 serviceState.callback = null;
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002355
Dongwon Kang426c9a42014-08-26 17:39:21 +09002356 abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002357 }
2358 }
Jae Seo39570912014-02-20 18:23:25 -08002359 }
2360 }
2361
2362 private final class ServiceCallback extends ITvInputServiceCallback.Stub {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002363 private final ComponentName mComponent;
Jae Seo39570912014-02-20 18:23:25 -08002364 private final int mUserId;
2365
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002366 ServiceCallback(ComponentName component, int userId) {
2367 mComponent = component;
Jae Seo39570912014-02-20 18:23:25 -08002368 mUserId = userId;
2369 }
2370
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002371 private void ensureHardwarePermission() {
2372 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2373 != PackageManager.PERMISSION_GRANTED) {
2374 throw new SecurityException("The caller does not have hardware permission");
2375 }
2376 }
Wonsik Kim187423c2014-06-25 14:12:48 +09002377
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002378 private void ensureValidInput(TvInputInfo inputInfo) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002379 if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002380 throw new IllegalArgumentException("Invalid TvInputInfo");
2381 }
2382 }
2383
Jae Seo1abbbcd2016-01-28 22:20:41 -08002384 private void addHardwareInputLocked(TvInputInfo inputInfo) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002385 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
Shubang71d5c762017-02-23 15:46:40 -08002386 serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +09002387 buildTvInputListLocked(mUserId, null);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002388 }
2389
Jae Seo1abbbcd2016-01-28 22:20:41 -08002390 public void addHardwareInput(int deviceId, TvInputInfo inputInfo) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002391 ensureHardwarePermission();
2392 ensureValidInput(inputInfo);
2393 synchronized (mLock) {
Jae Seo1abbbcd2016-01-28 22:20:41 -08002394 mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
2395 addHardwareInputLocked(inputInfo);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002396 }
2397 }
2398
Jae Seo1abbbcd2016-01-28 22:20:41 -08002399 public void addHdmiInput(int id, TvInputInfo inputInfo) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002400 ensureHardwarePermission();
2401 ensureValidInput(inputInfo);
2402 synchronized (mLock) {
Jae Seo1abbbcd2016-01-28 22:20:41 -08002403 mTvInputHardwareManager.addHdmiInput(id, inputInfo);
2404 addHardwareInputLocked(inputInfo);
Wonsik Kim187423c2014-06-25 14:12:48 +09002405 }
2406 }
2407
Jae Seo1abbbcd2016-01-28 22:20:41 -08002408 public void removeHardwareInput(String inputId) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002409 ensureHardwarePermission();
Wonsik Kim187423c2014-06-25 14:12:48 +09002410 synchronized (mLock) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002411 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
Shubang71d5c762017-02-23 15:46:40 -08002412 boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
Wonsik Kim187423c2014-06-25 14:12:48 +09002413 if (removed) {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +09002414 buildTvInputListLocked(mUserId, null);
Jae Seo1abbbcd2016-01-28 22:20:41 -08002415 mTvInputHardwareManager.removeHardwareInput(inputId);
Wonsik Kim187423c2014-06-25 14:12:48 +09002416 } else {
Jae Seofea8dd42014-08-26 13:57:41 -07002417 Slog.e(TAG, "failed to remove input " + inputId);
Wonsik Kim187423c2014-06-25 14:12:48 +09002418 }
Jae Seo39570912014-02-20 18:23:25 -08002419 }
2420 }
2421 }
Jae Seo31dc634be2014-04-15 17:40:23 -07002422
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002423 private final class SessionCallback extends ITvInputSessionCallback.Stub {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002424 private final SessionState mSessionState;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002425 private final InputChannel[] mChannels;
2426
2427 SessionCallback(SessionState sessionState, InputChannel[] channels) {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002428 mSessionState = sessionState;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002429 mChannels = channels;
2430 }
2431
2432 @Override
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002433 public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002434 if (DEBUG) {
Jae Seo2cdb05e2016-02-04 22:17:13 +09002435 Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")");
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002436 }
2437 synchronized (mLock) {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002438 mSessionState.session = session;
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002439 mSessionState.hardwareSessionToken = hardwareSessionToken;
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002440 if (session != null && addSessionTokenToClientStateLocked(session)) {
2441 sendSessionTokenToClientLocked(mSessionState.client,
Jae Seo2cdb05e2016-02-04 22:17:13 +09002442 mSessionState.inputId, mSessionState.sessionToken, mChannels[0],
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002443 mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002444 } else {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002445 removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId);
2446 sendSessionTokenToClientLocked(mSessionState.client,
Jae Seo2cdb05e2016-02-04 22:17:13 +09002447 mSessionState.inputId, null, null, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002448 }
2449 mChannels[0].dispose();
2450 }
2451 }
2452
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002453 private boolean addSessionTokenToClientStateLocked(ITvInputSession session) {
2454 try {
2455 session.asBinder().linkToDeath(mSessionState, 0);
2456 } catch (RemoteException e) {
2457 Slog.e(TAG, "session process has already died", e);
2458 return false;
2459 }
2460
2461 IBinder clientToken = mSessionState.client.asBinder();
Jae Seo4f1a6d42015-07-20 16:15:01 -07002462 UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002463 ClientState clientState = userState.clientStateMap.get(clientToken);
2464 if (clientState == null) {
2465 clientState = new ClientState(clientToken, mSessionState.userId);
2466 try {
2467 clientToken.linkToDeath(clientState, 0);
2468 } catch (RemoteException e) {
2469 Slog.e(TAG, "client process has already died", e);
2470 return false;
2471 }
2472 userState.clientStateMap.put(clientToken, clientState);
2473 }
2474 clientState.sessionTokens.add(mSessionState.sessionToken);
2475 return true;
2476 }
2477
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002478 @Override
2479 public void onChannelRetuned(Uri channelUri) {
2480 synchronized (mLock) {
2481 if (DEBUG) {
2482 Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
2483 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002484 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002485 return;
2486 }
2487 try {
2488 // TODO: Consider adding this channel change in the watch log. When we do
2489 // that, how we can protect the watch log from malicious tv inputs should
2490 // be addressed. e.g. add a field which represents where the channel change
2491 // originated from.
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002492 mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002493 } catch (RemoteException e) {
2494 Slog.e(TAG, "error in onChannelRetuned", e);
2495 }
2496 }
2497 }
2498
2499 @Override
2500 public void onTracksChanged(List<TvTrackInfo> tracks) {
2501 synchronized (mLock) {
2502 if (DEBUG) {
2503 Slog.d(TAG, "onTracksChanged(" + tracks + ")");
2504 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002505 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002506 return;
2507 }
2508 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002509 mSessionState.client.onTracksChanged(tracks, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002510 } catch (RemoteException e) {
2511 Slog.e(TAG, "error in onTracksChanged", e);
2512 }
2513 }
2514 }
2515
2516 @Override
2517 public void onTrackSelected(int type, String trackId) {
2518 synchronized (mLock) {
2519 if (DEBUG) {
2520 Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
2521 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002522 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002523 return;
2524 }
2525 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002526 mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002527 } catch (RemoteException e) {
2528 Slog.e(TAG, "error in onTrackSelected", e);
2529 }
2530 }
2531 }
2532
2533 @Override
2534 public void onVideoAvailable() {
2535 synchronized (mLock) {
2536 if (DEBUG) {
2537 Slog.d(TAG, "onVideoAvailable()");
2538 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002539 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002540 return;
2541 }
2542 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002543 mSessionState.client.onVideoAvailable(mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002544 } catch (RemoteException e) {
2545 Slog.e(TAG, "error in onVideoAvailable", e);
2546 }
2547 }
2548 }
2549
2550 @Override
2551 public void onVideoUnavailable(int reason) {
2552 synchronized (mLock) {
2553 if (DEBUG) {
2554 Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
2555 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002556 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002557 return;
2558 }
2559 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002560 mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002561 } catch (RemoteException e) {
2562 Slog.e(TAG, "error in onVideoUnavailable", e);
2563 }
2564 }
2565 }
2566
2567 @Override
2568 public void onContentAllowed() {
2569 synchronized (mLock) {
2570 if (DEBUG) {
2571 Slog.d(TAG, "onContentAllowed()");
2572 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002573 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002574 return;
2575 }
2576 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002577 mSessionState.client.onContentAllowed(mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002578 } catch (RemoteException e) {
2579 Slog.e(TAG, "error in onContentAllowed", e);
2580 }
2581 }
2582 }
2583
2584 @Override
2585 public void onContentBlocked(String rating) {
2586 synchronized (mLock) {
2587 if (DEBUG) {
2588 Slog.d(TAG, "onContentBlocked()");
2589 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002590 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002591 return;
2592 }
2593 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002594 mSessionState.client.onContentBlocked(rating, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002595 } catch (RemoteException e) {
2596 Slog.e(TAG, "error in onContentBlocked", e);
2597 }
2598 }
2599 }
2600
2601 @Override
2602 public void onLayoutSurface(int left, int top, int right, int bottom) {
2603 synchronized (mLock) {
2604 if (DEBUG) {
2605 Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
2606 + ", right=" + right + ", bottom=" + bottom + ",)");
2607 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002608 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002609 return;
2610 }
2611 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002612 mSessionState.client.onLayoutSurface(left, top, right, bottom,
2613 mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002614 } catch (RemoteException e) {
2615 Slog.e(TAG, "error in onLayoutSurface", e);
2616 }
2617 }
2618 }
2619
2620 @Override
2621 public void onSessionEvent(String eventType, Bundle eventArgs) {
2622 synchronized (mLock) {
2623 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002624 Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs
2625 + ")");
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002626 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002627 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002628 return;
2629 }
2630 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002631 mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002632 } catch (RemoteException e) {
2633 Slog.e(TAG, "error in onSessionEvent", e);
2634 }
2635 }
2636 }
Dongwon Kang6f0240c2015-03-31 17:56:36 -07002637
2638 @Override
2639 public void onTimeShiftStatusChanged(int status) {
2640 synchronized (mLock) {
2641 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002642 Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07002643 }
2644 if (mSessionState.session == null || mSessionState.client == null) {
2645 return;
2646 }
2647 try {
2648 mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq);
2649 } catch (RemoteException e) {
2650 Slog.e(TAG, "error in onTimeShiftStatusChanged", e);
2651 }
2652 }
2653 }
2654
2655 @Override
2656 public void onTimeShiftStartPositionChanged(long timeMs) {
2657 synchronized (mLock) {
2658 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002659 Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07002660 }
2661 if (mSessionState.session == null || mSessionState.client == null) {
2662 return;
2663 }
2664 try {
2665 mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq);
2666 } catch (RemoteException e) {
2667 Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e);
2668 }
2669 }
2670 }
2671
2672 @Override
2673 public void onTimeShiftCurrentPositionChanged(long timeMs) {
2674 synchronized (mLock) {
2675 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002676 Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07002677 }
2678 if (mSessionState.session == null || mSessionState.client == null) {
2679 return;
2680 }
2681 try {
2682 mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs,
2683 mSessionState.seq);
2684 } catch (RemoteException e) {
2685 Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e);
2686 }
2687 }
2688 }
Jae Seoa826d012016-01-18 13:03:35 -08002689
2690 // For the recording session only
2691 @Override
Dongwon Kangb55c7512016-03-01 09:36:07 -08002692 public void onTuned(Uri channelUri) {
Jae Seoa826d012016-01-18 13:03:35 -08002693 synchronized (mLock) {
2694 if (DEBUG) {
Jae Seoe3c11e82016-02-08 23:18:49 -08002695 Slog.d(TAG, "onTuned()");
Jae Seoa826d012016-01-18 13:03:35 -08002696 }
2697 if (mSessionState.session == null || mSessionState.client == null) {
2698 return;
2699 }
2700 try {
Dongwon Kangb55c7512016-03-01 09:36:07 -08002701 mSessionState.client.onTuned(mSessionState.seq, channelUri);
Jae Seoa826d012016-01-18 13:03:35 -08002702 } catch (RemoteException e) {
Jae Seoe3c11e82016-02-08 23:18:49 -08002703 Slog.e(TAG, "error in onTuned", e);
Jae Seoa826d012016-01-18 13:03:35 -08002704 }
2705 }
2706 }
2707
2708 // For the recording session only
2709 @Override
2710 public void onRecordingStopped(Uri recordedProgramUri) {
2711 synchronized (mLock) {
2712 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002713 Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri
2714 + ")");
Jae Seoa826d012016-01-18 13:03:35 -08002715 }
2716 if (mSessionState.session == null || mSessionState.client == null) {
2717 return;
2718 }
2719 try {
2720 mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq);
2721 } catch (RemoteException e) {
2722 Slog.e(TAG, "error in onRecordingStopped", e);
2723 }
2724 }
2725 }
2726
2727 // For the recording session only
2728 @Override
2729 public void onError(int error) {
2730 synchronized (mLock) {
2731 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002732 Slog.d(TAG, "onError(error=" + error + ")");
Jae Seoa826d012016-01-18 13:03:35 -08002733 }
2734 if (mSessionState.session == null || mSessionState.client == null) {
2735 return;
2736 }
2737 try {
2738 mSessionState.client.onError(error, mSessionState.seq);
2739 } catch (RemoteException e) {
2740 Slog.e(TAG, "error in onError", e);
2741 }
2742 }
2743 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002744 }
2745
2746 private static final class WatchLogHandler extends Handler {
Jae Seo7eb75df2014-08-08 22:20:48 -07002747 // There are only two kinds of watch events that can happen on the system:
2748 // 1. The current TV input session is tuned to a new channel.
2749 // 2. The session is released for some reason.
2750 // The former indicates the end of the previous log entry, if any, followed by the start of
2751 // a new entry. The latter indicates the end of the most recent entry for the given session.
2752 // Here the system supplies the database the smallest set of information only that is
2753 // sufficient to consolidate the log entries while minimizing database operations in the
2754 // system service.
Jae Seo8c375fe2015-06-23 20:33:25 -07002755 static final int MSG_LOG_WATCH_START = 1;
2756 static final int MSG_LOG_WATCH_END = 2;
2757 static final int MSG_SWITCH_CONTENT_RESOLVER = 3;
Jae Seo31dc634be2014-04-15 17:40:23 -07002758
Jae Seo8c375fe2015-06-23 20:33:25 -07002759 private ContentResolver mContentResolver;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002760
Jae Seo8c375fe2015-06-23 20:33:25 -07002761 WatchLogHandler(ContentResolver contentResolver, Looper looper) {
Jae Seo31dc634be2014-04-15 17:40:23 -07002762 super(looper);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002763 mContentResolver = contentResolver;
Jae Seo31dc634be2014-04-15 17:40:23 -07002764 }
2765
2766 @Override
2767 public void handleMessage(Message msg) {
2768 switch (msg.what) {
Jae Seo7eb75df2014-08-08 22:20:48 -07002769 case MSG_LOG_WATCH_START: {
Jae Seo31dc634be2014-04-15 17:40:23 -07002770 SomeArgs args = (SomeArgs) msg.obj;
Jae Seo7eb75df2014-08-08 22:20:48 -07002771 String packageName = (String) args.arg1;
2772 long watchStartTime = (long) args.arg2;
2773 long channelId = (long) args.arg3;
2774 Bundle tuneParams = (Bundle) args.arg4;
2775 IBinder sessionToken = (IBinder) args.arg5;
2776
2777 ContentValues values = new ContentValues();
2778 values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
2779 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
2780 watchStartTime);
2781 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
2782 if (tuneParams != null) {
2783 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS,
2784 encodeTuneParams(tuneParams));
2785 }
2786 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
2787 sessionToken.toString());
2788
2789 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
Jae Seo31dc634be2014-04-15 17:40:23 -07002790 args.recycle();
Jae Seo8c375fe2015-06-23 20:33:25 -07002791 break;
Jae Seo31dc634be2014-04-15 17:40:23 -07002792 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002793 case MSG_LOG_WATCH_END: {
Jae Seo31dc634be2014-04-15 17:40:23 -07002794 SomeArgs args = (SomeArgs) msg.obj;
Jae Seo7eb75df2014-08-08 22:20:48 -07002795 IBinder sessionToken = (IBinder) args.arg1;
2796 long watchEndTime = (long) args.arg2;
2797
2798 ContentValues values = new ContentValues();
2799 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
2800 watchEndTime);
2801 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
2802 sessionToken.toString());
2803
2804 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
Jae Seo31dc634be2014-04-15 17:40:23 -07002805 args.recycle();
Jae Seo8c375fe2015-06-23 20:33:25 -07002806 break;
2807 }
2808 case MSG_SWITCH_CONTENT_RESOLVER: {
2809 mContentResolver = (ContentResolver) msg.obj;
2810 break;
Jae Seo31dc634be2014-04-15 17:40:23 -07002811 }
2812 default: {
Jae Seo8c375fe2015-06-23 20:33:25 -07002813 Slog.w(TAG, "unhandled message code: " + msg.what);
2814 break;
Jae Seo31dc634be2014-04-15 17:40:23 -07002815 }
2816 }
2817 }
2818
Jae Seo7eb75df2014-08-08 22:20:48 -07002819 private String encodeTuneParams(Bundle tuneParams) {
2820 StringBuilder builder = new StringBuilder();
2821 Set<String> keySet = tuneParams.keySet();
2822 Iterator<String> it = keySet.iterator();
2823 while (it.hasNext()) {
2824 String key = it.next();
2825 Object value = tuneParams.get(key);
2826 if (value == null) {
2827 continue;
Jae Seo579befe2014-08-06 19:18:37 -07002828 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002829 builder.append(replaceEscapeCharacters(key));
2830 builder.append("=");
2831 builder.append(replaceEscapeCharacters(value.toString()));
2832 if (it.hasNext()) {
2833 builder.append(", ");
Jae Seo31dc634be2014-04-15 17:40:23 -07002834 }
2835 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002836 return builder.toString();
Jae Seo31dc634be2014-04-15 17:40:23 -07002837 }
2838
Jae Seo7eb75df2014-08-08 22:20:48 -07002839 private String replaceEscapeCharacters(String src) {
2840 final char ESCAPE_CHARACTER = '%';
2841 final String ENCODING_TARGET_CHARACTERS = "%=,";
2842 StringBuilder builder = new StringBuilder();
2843 for (char ch : src.toCharArray()) {
2844 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) {
2845 builder.append(ESCAPE_CHARACTER);
Chulwoo Lee8d4ded02014-07-10 03:56:39 +09002846 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002847 builder.append(ch);
Jae Seo31dc634be2014-04-15 17:40:23 -07002848 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002849 return builder.toString();
Jae Seo579befe2014-08-06 19:18:37 -07002850 }
Jae Seo31dc634be2014-04-15 17:40:23 -07002851 }
Wonsik Kim969167d2014-06-24 16:33:17 +09002852
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002853 private final class HardwareListener implements TvInputHardwareManager.Listener {
Wonsik Kim187423c2014-06-25 14:12:48 +09002854 @Override
2855 public void onStateChanged(String inputId, int state) {
Wonsik Kim969167d2014-06-24 16:33:17 +09002856 synchronized (mLock) {
2857 setStateLocked(inputId, state, mCurrentUserId);
2858 }
2859 }
Wonsik Kim187423c2014-06-25 14:12:48 +09002860
2861 @Override
2862 public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
2863 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002864 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
Wonsik Kim187423c2014-06-25 14:12:48 +09002865 // Broadcast the event to all hardware inputs.
2866 for (ServiceState serviceState : userState.serviceStateMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002867 if (!serviceState.isHardware || serviceState.service == null) continue;
Wonsik Kim187423c2014-06-25 14:12:48 +09002868 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002869 serviceState.service.notifyHardwareAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09002870 } catch (RemoteException e) {
2871 Slog.e(TAG, "error in notifyHardwareAdded", e);
2872 }
2873 }
2874 }
2875 }
2876
2877 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002878 public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
Wonsik Kim187423c2014-06-25 14:12:48 +09002879 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002880 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
Wonsik Kim187423c2014-06-25 14:12:48 +09002881 // Broadcast the event to all hardware inputs.
2882 for (ServiceState serviceState : userState.serviceStateMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002883 if (!serviceState.isHardware || serviceState.service == null) continue;
Wonsik Kim187423c2014-06-25 14:12:48 +09002884 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002885 serviceState.service.notifyHardwareRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09002886 } catch (RemoteException e) {
2887 Slog.e(TAG, "error in notifyHardwareRemoved", e);
2888 }
2889 }
2890 }
2891 }
2892
2893 @Override
Jae Seo546c6352014-08-07 11:57:01 -07002894 public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
Wonsik Kim187423c2014-06-25 14:12:48 +09002895 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002896 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002897 // Broadcast the event to all hardware inputs.
2898 for (ServiceState serviceState : userState.serviceStateMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002899 if (!serviceState.isHardware || serviceState.service == null) continue;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002900 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002901 serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002902 } catch (RemoteException e) {
Jae Seo546c6352014-08-07 11:57:01 -07002903 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002904 }
2905 }
Wonsik Kim187423c2014-06-25 14:12:48 +09002906 }
2907 }
2908
2909 @Override
Jae Seo546c6352014-08-07 11:57:01 -07002910 public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
Wonsik Kim187423c2014-06-25 14:12:48 +09002911 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002912 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002913 // Broadcast the event to all hardware inputs.
2914 for (ServiceState serviceState : userState.serviceStateMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002915 if (!serviceState.isHardware || serviceState.service == null) continue;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002916 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002917 serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002918 } catch (RemoteException e) {
Jae Seo546c6352014-08-07 11:57:01 -07002919 Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002920 }
2921 }
Wonsik Kim187423c2014-06-25 14:12:48 +09002922 }
2923 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09002924
2925 @Override
Wonsik Kime92f8572014-08-12 18:30:24 +09002926 public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
2927 synchronized (mLock) {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002928 Integer state;
Wonsik Kime92f8572014-08-12 18:30:24 +09002929 switch (deviceInfo.getDevicePowerStatus()) {
2930 case HdmiControlManager.POWER_STATUS_ON:
2931 state = INPUT_STATE_CONNECTED;
2932 break;
2933 case HdmiControlManager.POWER_STATUS_STANDBY:
2934 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
2935 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
2936 state = INPUT_STATE_CONNECTED_STANDBY;
2937 break;
2938 case HdmiControlManager.POWER_STATUS_UNKNOWN:
2939 default:
2940 state = null;
2941 break;
2942 }
2943 if (state != null) {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002944 setStateLocked(inputId, state, mCurrentUserId);
Wonsik Kime92f8572014-08-12 18:30:24 +09002945 }
Kyeongkab.Namf6d1e092019-11-25 10:16:04 +09002946 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
2947 // Broadcast the event to all hardware inputs.
2948 for (ServiceState serviceState : userState.serviceStateMap.values()) {
2949 if (!serviceState.isHardware || serviceState.service == null) continue;
2950 try {
2951 serviceState.service.notifyHdmiDeviceUpdated(deviceInfo);
2952 } catch (RemoteException e) {
2953 Slog.e(TAG, "error in notifyHdmiDeviceUpdated", e);
2954 }
2955 }
Wonsik Kime92f8572014-08-12 18:30:24 +09002956 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09002957 }
Wonsik Kim969167d2014-06-24 16:33:17 +09002958 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +09002959
2960 private static class SessionNotFoundException extends IllegalArgumentException {
Dongwon Kangfdce9e52014-12-04 18:08:00 +09002961 public SessionNotFoundException(String name) {
2962 super(name);
2963 }
2964 }
Jae Seo39570912014-02-20 18:23:25 -08002965}