blob: 1afde550f02711d9e3f690ccc4d37b964282a186 [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;
70import android.os.RemoteException;
71import android.os.UserHandle;
Jae Seoc2a89512016-01-28 10:38:11 -080072import android.text.TextUtils;
Youngsang Cho9a22f0f2014-04-09 22:51:54 +090073import android.util.Slog;
Jae Seo39570912014-02-20 18:23:25 -080074import android.util.SparseArray;
Jae Seo6a6059a2014-04-17 21:35:29 -070075import android.view.InputChannel;
Jae Seo39570912014-02-20 18:23:25 -080076import android.view.Surface;
77
78import com.android.internal.content.PackageMonitor;
Jae Seo31dc634be2014-04-15 17:40:23 -070079import com.android.internal.os.SomeArgs;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060080import com.android.internal.util.DumpUtils;
Jaewan Kime14c3f42014-06-27 13:47:48 +090081import com.android.internal.util.IndentingPrintWriter;
Jae Seo31dc634be2014-04-15 17:40:23 -070082import com.android.server.IoThread;
Jae Seo39570912014-02-20 18:23:25 -080083import com.android.server.SystemService;
84
Jaesung Chung58739e72015-04-24 19:39:59 +090085import java.io.File;
Jaewan Kime14c3f42014-06-27 13:47:48 +090086import java.io.FileDescriptor;
Jaesung Chung58739e72015-04-24 19:39:59 +090087import java.io.FileNotFoundException;
Jaewan Kime14c3f42014-06-27 13:47:48 +090088import java.io.PrintWriter;
Jae Seo39570912014-02-20 18:23:25 -080089import java.util.ArrayList;
Chulwoo Lee19ba61a2014-09-03 00:59:35 +090090import java.util.Arrays;
Jaesung Chung58739e72015-04-24 19:39:59 +090091import java.util.Collections;
Jae Seo39570912014-02-20 18:23:25 -080092import java.util.HashMap;
Jae Seo5c80ad22014-06-12 19:52:58 -070093import java.util.HashSet;
Wonsik Kim187423c2014-06-25 14:12:48 +090094import java.util.Iterator;
Jae Seo39570912014-02-20 18:23:25 -080095import java.util.List;
96import java.util.Map;
Jae Seo5c80ad22014-06-12 19:52:58 -070097import java.util.Set;
Jaesung Chung58739e72015-04-24 19:39:59 +090098import java.util.regex.Matcher;
99import java.util.regex.Pattern;
Jae Seo39570912014-02-20 18:23:25 -0800100
101/** This class provides a system service that manages television inputs. */
102public final class TvInputManagerService extends SystemService {
Jae Seoee2ec052014-09-14 10:30:05 -0700103 private static final boolean DEBUG = false;
Jae Seo39570912014-02-20 18:23:25 -0800104 private static final String TAG = "TvInputManagerService";
Jiabinef1659f2016-11-18 10:50:15 +0900105 private static final String DVB_DIRECTORY = "/dev/dvb";
Jae Seo39570912014-02-20 18:23:25 -0800106
Jiabinef1659f2016-11-18 10:50:15 +0900107 // There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
108 // another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
109 // DVB frontend devices from the list of files in the /dev and /dev/dvb/adapter%d directory.
Jaesung Chung58739e72015-04-24 19:39:59 +0900110 private static final Pattern sFrontEndDevicePattern =
111 Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$");
Jiabinef1659f2016-11-18 10:50:15 +0900112 private static final Pattern sAdapterDirPattern =
113 Pattern.compile("^adapter([0-9]+)$");
114 private static final Pattern sFrontEndInAdapterDirPattern =
115 Pattern.compile("^frontend([0-9]+)$");
Jaesung Chung58739e72015-04-24 19:39:59 +0900116
Jae Seo39570912014-02-20 18:23:25 -0800117 private final Context mContext;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000118 private final TvInputHardwareManager mTvInputHardwareManager;
Jae Seo39570912014-02-20 18:23:25 -0800119
120 // A global lock.
121 private final Object mLock = new Object();
122
123 // ID of the current user.
Xiaohui Chen233d94c2015-07-30 15:08:00 -0700124 private int mCurrentUserId = UserHandle.USER_SYSTEM;
Jae Seo39570912014-02-20 18:23:25 -0800125
126 // A map from user id to UserState.
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700127 private final SparseArray<UserState> mUserStates = new SparseArray<>();
Jae Seo39570912014-02-20 18:23:25 -0800128
Jae Seo7eb75df2014-08-08 22:20:48 -0700129 private final WatchLogHandler mWatchLogHandler;
Jae Seo31dc634be2014-04-15 17:40:23 -0700130
Jae Seo39570912014-02-20 18:23:25 -0800131 public TvInputManagerService(Context context) {
132 super(context);
Jae Seo31dc634be2014-04-15 17:40:23 -0700133
Jae Seo39570912014-02-20 18:23:25 -0800134 mContext = context;
Jae Seo8c375fe2015-06-23 20:33:25 -0700135 mWatchLogHandler = new WatchLogHandler(mContext.getContentResolver(),
136 IoThread.get().getLooper());
Wonsik Kim187423c2014-06-25 14:12:48 +0900137 mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
Jae Seo31dc634be2014-04-15 17:40:23 -0700138
Jae Seo39570912014-02-20 18:23:25 -0800139 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700140 getOrCreateUserStateLocked(mCurrentUserId);
Jae Seo39570912014-02-20 18:23:25 -0800141 }
142 }
143
144 @Override
145 public void onStart() {
146 publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
147 }
148
Ji-Hwan Lee0ceb7e42014-06-21 04:42:05 +0900149 @Override
150 public void onBootPhase(int phase) {
151 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
152 registerBroadcastReceivers();
Wonsik Kim187423c2014-06-25 14:12:48 +0900153 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
Ji-Hwan Lee0ceb7e42014-06-21 04:42:05 +0900154 synchronized (mLock) {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900155 buildTvInputListLocked(mCurrentUserId, null);
Jae Seo9c165d62014-08-25 14:39:26 -0700156 buildTvContentRatingSystemListLocked(mCurrentUserId);
Ji-Hwan Lee0ceb7e42014-06-21 04:42:05 +0900157 }
158 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900159 mTvInputHardwareManager.onBootPhase(phase);
Ji-Hwan Lee0ceb7e42014-06-21 04:42:05 +0900160 }
161
Youngsang Cho19c47d72016-05-05 09:21:24 -0700162 @Override
163 public void onUnlockUser(int userHandle) {
164 if (DEBUG) Slog.d(TAG, "onUnlockUser(userHandle=" + userHandle + ")");
165 synchronized (mLock) {
166 if (mCurrentUserId != userHandle) {
167 return;
168 }
169 buildTvInputListLocked(mCurrentUserId, null);
170 buildTvContentRatingSystemListLocked(mCurrentUserId);
171 }
172 }
173
Jae Seo39570912014-02-20 18:23:25 -0800174 private void registerBroadcastReceivers() {
175 PackageMonitor monitor = new PackageMonitor() {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900176 private void buildTvInputList(String[] packages) {
177 synchronized (mLock) {
Dongwon Kanga897e5f2016-04-19 13:39:47 -0700178 if (mCurrentUserId == getChangingUserId()) {
179 buildTvInputListLocked(mCurrentUserId, packages);
180 buildTvContentRatingSystemListLocked(mCurrentUserId);
181 }
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900182 }
183 }
184
185 @Override
186 public void onPackageUpdateFinished(String packageName, int uid) {
187 if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
188 // This callback is invoked when the TV input is reinstalled.
189 // In this case, isReplacing() always returns true.
190 buildTvInputList(new String[] { packageName });
191 }
192
193 @Override
194 public void onPackagesAvailable(String[] packages) {
195 if (DEBUG) {
196 Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
197 }
198 // This callback is invoked when the media on which some packages exist become
199 // available.
200 if (isReplacing()) {
201 buildTvInputList(packages);
202 }
203 }
204
205 @Override
206 public void onPackagesUnavailable(String[] packages) {
207 // This callback is invoked when the media on which some packages exist become
208 // unavailable.
209 if (DEBUG) {
210 Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
211 + ")");
212 }
213 if (isReplacing()) {
214 buildTvInputList(packages);
215 }
216 }
217
Jae Seo39570912014-02-20 18:23:25 -0800218 @Override
219 public void onSomePackagesChanged() {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900220 // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
221 // the TV inputs.
Dongwon Kang426c9a42014-08-26 17:39:21 +0900222 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900223 if (isReplacing()) {
224 if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
225 // When the package is updated, buildTvInputListLocked is called in other
226 // methods instead.
227 return;
Jae Seo39570912014-02-20 18:23:25 -0800228 }
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900229 buildTvInputList(null);
Jae Seo39570912014-02-20 18:23:25 -0800230 }
Jae Seo5c80ad22014-06-12 19:52:58 -0700231
232 @Override
Dongwon Kang31a8f842015-04-08 18:26:23 -0700233 public boolean onPackageChanged(String packageName, int uid, String[] components) {
234 // The input list needs to be updated in any cases, regardless of whether
235 // it happened to the whole package or a specific component. Returning true so that
236 // the update can be handled in {@link #onSomePackagesChanged}.
237 return true;
238 }
Jae Seo39570912014-02-20 18:23:25 -0800239 };
240 monitor.register(mContext, null, UserHandle.ALL, true);
241
242 IntentFilter intentFilter = new IntentFilter();
243 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
244 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
245 mContext.registerReceiverAsUser(new BroadcastReceiver() {
246 @Override
247 public void onReceive(Context context, Intent intent) {
248 String action = intent.getAction();
249 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
250 switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
251 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
252 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
253 }
254 }
255 }, UserHandle.ALL, intentFilter, null, null);
256 }
257
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900258 private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900259 return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE,
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900260 component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
Wonsik Kim187423c2014-06-25 14:12:48 +0900261 }
262
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900263 private void buildTvInputListLocked(int userId, String[] updatedPackages) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700264 UserState userState = getOrCreateUserStateLocked(userId);
Wonsik Kim969167d2014-06-24 16:33:17 +0900265 userState.packageSet.clear();
Jae Seo39570912014-02-20 18:23:25 -0800266
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900267 if (DEBUG) Slog.d(TAG, "buildTvInputList");
Jae Seo39570912014-02-20 18:23:25 -0800268 PackageManager pm = mContext.getPackageManager();
Jae Seo76976fa2015-05-20 21:51:16 -0700269 List<ResolveInfo> services = pm.queryIntentServicesAsUser(
Chulwoo Leee7bb7d62014-05-27 14:10:37 +0900270 new Intent(TvInputService.SERVICE_INTERFACE),
Jae Seo76976fa2015-05-20 21:51:16 -0700271 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
272 userId);
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700273 List<TvInputInfo> inputList = new ArrayList<>();
Jae Seo39570912014-02-20 18:23:25 -0800274 for (ResolveInfo ri : services) {
275 ServiceInfo si = ri.serviceInfo;
276 if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +0900277 Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
Jae Seo39570912014-02-20 18:23:25 -0800278 + android.Manifest.permission.BIND_TV_INPUT);
279 continue;
280 }
Jae Seo9cc28e52014-08-12 16:45:58 -0700281
282 ComponentName component = new ComponentName(si.packageName, si.name);
283 if (hasHardwarePermission(pm, component)) {
284 ServiceState serviceState = userState.serviceStateMap.get(component);
285 if (serviceState == null) {
Jae Seoc980f43d2016-02-09 23:46:58 -0800286 // New hardware input found. Create a new ServiceState and connect to the
287 // service to populate the hardware list.
Jae Seo9cc28e52014-08-12 16:45:58 -0700288 serviceState = new ServiceState(component, userId);
289 userState.serviceStateMap.put(component, serviceState);
Wonsik Kimf271eac2014-08-30 12:55:10 +0900290 updateServiceConnectionLocked(component, userId);
Wonsik Kim187423c2014-06-25 14:12:48 +0900291 } else {
Shubang71d5c762017-02-23 15:46:40 -0800292 inputList.addAll(serviceState.hardwareInputMap.values());
Jae Seo9cc28e52014-08-12 16:45:58 -0700293 }
294 } else {
295 try {
Jae Seoc03671f2016-01-26 15:15:22 -0800296 TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
297 inputList.add(info);
Jae Seo18c0cfb2016-05-16 18:19:11 -0700298 } catch (Exception e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700299 Slog.e(TAG, "failed to load TV input " + si.name, e);
Jae Seo9cc28e52014-08-12 16:45:58 -0700300 continue;
Wonsik Kim969167d2014-06-24 16:33:17 +0900301 }
Chulwoo Leee7bb7d62014-05-27 14:10:37 +0900302 }
Jae Seo9cc28e52014-08-12 16:45:58 -0700303 userState.packageSet.add(si.packageName);
304 }
305
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700306 Map<String, TvInputState> inputMap = new HashMap<>();
Jae Seo9cc28e52014-08-12 16:45:58 -0700307 for (TvInputInfo info : inputList) {
Jae Seofea8dd42014-08-26 13:57:41 -0700308 if (DEBUG) {
309 Slog.d(TAG, "add " + info.getId());
310 }
Jae Seoabda4202016-01-28 19:13:04 -0800311 TvInputState inputState = userState.inputMap.get(info.getId());
312 if (inputState == null) {
313 inputState = new TvInputState();
Jae Seo9cc28e52014-08-12 16:45:58 -0700314 }
Shubang2263fb02016-05-13 16:51:53 -0700315 inputState.info = info;
Jae Seoabda4202016-01-28 19:13:04 -0800316 inputMap.put(info.getId(), inputState);
Jae Seo39570912014-02-20 18:23:25 -0800317 }
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900318
319 for (String inputId : inputMap.keySet()) {
320 if (!userState.inputMap.containsKey(inputId)) {
321 notifyInputAddedLocked(userState, inputId);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900322 } else if (updatedPackages != null) {
323 // Notify the package updates
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +0900324 ComponentName component = inputMap.get(inputId).info.getComponent();
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900325 for (String updatedPackage : updatedPackages) {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +0900326 if (component.getPackageName().equals(updatedPackage)) {
327 updateServiceConnectionLocked(component, userId);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900328 notifyInputUpdatedLocked(userState, inputId);
329 break;
330 }
331 }
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900332 }
333 }
334
335 for (String inputId : userState.inputMap.keySet()) {
336 if (!inputMap.containsKey(inputId)) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900337 TvInputInfo info = userState.inputMap.get(inputId).info;
Dongwon Kang426c9a42014-08-26 17:39:21 +0900338 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
339 if (serviceState != null) {
340 abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
341 }
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900342 notifyInputRemovedLocked(userState, inputId);
343 }
344 }
345
346 userState.inputMap.clear();
347 userState.inputMap = inputMap;
Jae Seo9c165d62014-08-25 14:39:26 -0700348 }
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900349
Jae Seo9c165d62014-08-25 14:39:26 -0700350 private void buildTvContentRatingSystemListLocked(int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700351 UserState userState = getOrCreateUserStateLocked(userId);
Jae Seo9c165d62014-08-25 14:39:26 -0700352 userState.contentRatingSystemList.clear();
353
354 final PackageManager pm = mContext.getPackageManager();
355 Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS);
356 for (ResolveInfo resolveInfo :
357 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) {
358 ActivityInfo receiver = resolveInfo.activityInfo;
359 Bundle metaData = receiver.metaData;
360 if (metaData == null) {
361 continue;
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900362 }
Jae Seo9c165d62014-08-25 14:39:26 -0700363
364 int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS);
365 if (xmlResId == 0) {
366 Slog.w(TAG, "Missing meta-data '"
367 + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver "
368 + receiver.packageName + "/" + receiver.name);
369 continue;
370 }
371 userState.contentRatingSystemList.add(
372 TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId,
373 receiver.applicationInfo));
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900374 }
Jae Seo39570912014-02-20 18:23:25 -0800375 }
376
377 private void switchUser(int userId) {
378 synchronized (mLock) {
379 if (mCurrentUserId == userId) {
380 return;
381 }
shubang8049f202016-04-25 11:21:42 -0700382 UserState userState = mUserStates.get(mCurrentUserId);
383 List<SessionState> sessionStatesToRelease = new ArrayList<>();
384 for (SessionState sessionState : userState.sessionStateMap.values()) {
385 if (sessionState.session != null && !sessionState.isRecordingSession) {
386 sessionStatesToRelease.add(sessionState);
387 }
388 }
389 for (SessionState sessionState : sessionStatesToRelease) {
390 try {
391 sessionState.session.release();
392 } catch (RemoteException e) {
393 Slog.e(TAG, "error in release", e);
394 }
395 clearSessionAndNotifyClientLocked(sessionState);
396 }
397
398 for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator();
399 it.hasNext(); ) {
400 ComponentName component = it.next();
401 ServiceState serviceState = userState.serviceStateMap.get(component);
402 if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
403 if (serviceState.callback != null) {
404 try {
405 serviceState.service.unregisterCallback(serviceState.callback);
406 } catch (RemoteException e) {
407 Slog.e(TAG, "error in unregisterCallback", e);
408 }
409 }
410 mContext.unbindService(serviceState.connection);
411 it.remove();
412 }
413 }
Jae Seo39570912014-02-20 18:23:25 -0800414
Jae Seo8c375fe2015-06-23 20:33:25 -0700415 mCurrentUserId = userId;
Jae Seo4f1a6d42015-07-20 16:15:01 -0700416 getOrCreateUserStateLocked(userId);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900417 buildTvInputListLocked(userId, null);
Jae Seo9c165d62014-08-25 14:39:26 -0700418 buildTvContentRatingSystemListLocked(userId);
Jae Seo8c375fe2015-06-23 20:33:25 -0700419 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_SWITCH_CONTENT_RESOLVER,
420 getContentResolverForUser(userId)).sendToTarget();
Jae Seo39570912014-02-20 18:23:25 -0800421 }
422 }
423
shubang8049f202016-04-25 11:21:42 -0700424 private void clearSessionAndNotifyClientLocked(SessionState state) {
425 if (state.client != null) {
426 try {
427 state.client.onSessionReleased(state.seq);
428 } catch(RemoteException e) {
429 Slog.e(TAG, "error in onSessionReleased", e);
430 }
431 }
432 // If there are any other sessions based on this session, they should be released.
433 UserState userState = getOrCreateUserStateLocked(state.userId);
434 for (SessionState sessionState : userState.sessionStateMap.values()) {
435 if (state.sessionToken == sessionState.hardwareSessionToken) {
436 releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID, state.userId);
437 try {
438 sessionState.client.onSessionReleased(sessionState.seq);
439 } catch (RemoteException e) {
440 Slog.e(TAG, "error in onSessionReleased", e);
441 }
442 }
443 }
444 removeSessionStateLocked(state.sessionToken, state.userId);
445 }
446
Jae Seo39570912014-02-20 18:23:25 -0800447 private void removeUser(int userId) {
448 synchronized (mLock) {
Jae Seob06cb882014-04-09 12:08:17 -0700449 UserState userState = mUserStates.get(userId);
450 if (userState == null) {
451 return;
452 }
shubang8049f202016-04-25 11:21:42 -0700453 // Release all created sessions.
454 for (SessionState state : userState.sessionStateMap.values()) {
455 if (state.session != null) {
456 try {
457 state.session.release();
458 } catch (RemoteException e) {
459 Slog.e(TAG, "error in release", e);
460 }
461 }
462 }
463 userState.sessionStateMap.clear();
464
465 // Unregister all callbacks and unbind all services.
466 for (ServiceState serviceState : userState.serviceStateMap.values()) {
467 if (serviceState.service != null) {
468 if (serviceState.callback != null) {
469 try {
470 serviceState.service.unregisterCallback(serviceState.callback);
471 } catch (RemoteException e) {
472 Slog.e(TAG, "error in unregisterCallback", e);
473 }
474 }
475 mContext.unbindService(serviceState.connection);
476 }
477 }
478 userState.serviceStateMap.clear();
Jae Seo39570912014-02-20 18:23:25 -0800479
Jae Seofea8dd42014-08-26 13:57:41 -0700480 // Clear everything else.
481 userState.inputMap.clear();
482 userState.packageSet.clear();
Jae Seo9c165d62014-08-25 14:39:26 -0700483 userState.contentRatingSystemList.clear();
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900484 userState.clientStateMap.clear();
Jae Seofea8dd42014-08-26 13:57:41 -0700485 userState.callbackSet.clear();
486 userState.mainSessionToken = null;
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900487
Jae Seo39570912014-02-20 18:23:25 -0800488 mUserStates.remove(userId);
489 }
490 }
491
Jae Seo8c375fe2015-06-23 20:33:25 -0700492 private ContentResolver getContentResolverForUser(int userId) {
493 UserHandle user = new UserHandle(userId);
494 Context context;
495 try {
496 context = mContext.createPackageContextAsUser("android", 0, user);
497 } catch (NameNotFoundException e) {
Jae Seo2a2b2992016-01-12 23:13:14 -0800498 Slog.e(TAG, "failed to create package context as user " + user);
Jae Seo8c375fe2015-06-23 20:33:25 -0700499 context = mContext;
500 }
501 return context.getContentResolver();
502 }
503
Jae Seo4f1a6d42015-07-20 16:15:01 -0700504 private UserState getOrCreateUserStateLocked(int userId) {
Jae Seo39570912014-02-20 18:23:25 -0800505 UserState userState = mUserStates.get(userId);
506 if (userState == null) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700507 userState = new UserState(mContext, userId);
508 mUserStates.put(userId, userState);
Jae Seo39570912014-02-20 18:23:25 -0800509 }
510 return userState;
511 }
512
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900513 private ServiceState getServiceStateLocked(ComponentName component, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700514 UserState userState = getOrCreateUserStateLocked(userId);
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900515 ServiceState serviceState = userState.serviceStateMap.get(component);
Jae Seo39570912014-02-20 18:23:25 -0800516 if (serviceState == null) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900517 throw new IllegalStateException("Service state not found for " + component + " (userId="
Sungsoo Lim7de5e232014-04-12 16:51:27 +0900518 + userId + ")");
Jae Seo39570912014-02-20 18:23:25 -0800519 }
520 return serviceState;
521 }
522
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900523 private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700524 UserState userState = getOrCreateUserStateLocked(userId);
Jae Seo39570912014-02-20 18:23:25 -0800525 SessionState sessionState = userState.sessionStateMap.get(sessionToken);
526 if (sessionState == null) {
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900527 throw new SessionNotFoundException("Session state not found for token " + sessionToken);
Jae Seo39570912014-02-20 18:23:25 -0800528 }
529 // Only the application that requested this session or the system can access it.
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900530 if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
Jae Seo39570912014-02-20 18:23:25 -0800531 throw new SecurityException("Illegal access to the session with token " + sessionToken
532 + " from uid " + callingUid);
533 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900534 return sessionState;
535 }
536
537 private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
Ji-Hwan Lee4c526972014-07-22 04:46:30 +0900538 return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
539 }
540
541 private ITvInputSession getSessionLocked(SessionState sessionState) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900542 ITvInputSession session = sessionState.session;
Jae Seo39570912014-02-20 18:23:25 -0800543 if (session == null) {
Ji-Hwan Lee4c526972014-07-22 04:46:30 +0900544 throw new IllegalStateException("Session not yet created for token "
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900545 + sessionState.sessionToken);
Jae Seo39570912014-02-20 18:23:25 -0800546 }
547 return session;
548 }
549
550 private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
551 String methodName) {
552 return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
553 false, methodName, null);
554 }
555
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900556 private void updateServiceConnectionLocked(ComponentName component, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700557 UserState userState = getOrCreateUserStateLocked(userId);
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900558 ServiceState serviceState = userState.serviceStateMap.get(component);
Jae Seo39570912014-02-20 18:23:25 -0800559 if (serviceState == null) {
560 return;
561 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900562 if (serviceState.reconnecting) {
563 if (!serviceState.sessionTokens.isEmpty()) {
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900564 // wait until all the sessions are removed.
565 return;
566 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900567 serviceState.reconnecting = false;
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900568 }
shubang8049f202016-04-25 11:21:42 -0700569
570 boolean shouldBind;
571 if (userId == mCurrentUserId) {
572 shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
573 } else {
574 // For a non-current user,
575 // if sessionTokens is not empty, it contains recording sessions only
576 // because other sessions must have been removed while switching user
577 // and non-recording sessions are not created by createSession().
578 shouldBind = !serviceState.sessionTokens.isEmpty();
579 }
580
581 if (serviceState.service == null && shouldBind) {
Jae Seo39570912014-02-20 18:23:25 -0800582 // This means that the service is not yet connected but its state indicates that we
583 // have pending requests. Then, connect the service.
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900584 if (serviceState.bound) {
Jae Seo39570912014-02-20 18:23:25 -0800585 // We have already bound to the service so we don't try to bind again until after we
586 // unbind later on.
587 return;
588 }
589 if (DEBUG) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900590 Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
Jae Seo39570912014-02-20 18:23:25 -0800591 }
Sungsoo Limd6672b52014-04-30 10:43:26 +0900592
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900593 Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900594 serviceState.bound = mContext.bindServiceAsUser(
Dianne Hackbornd69e4c12015-04-24 09:54:54 -0700595 i, serviceState.connection,
596 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
597 new UserHandle(userId));
shubang8049f202016-04-25 11:21:42 -0700598 } else if (serviceState.service != null && !shouldBind) {
Jae Seo39570912014-02-20 18:23:25 -0800599 // This means that the service is already connected but its state indicates that we have
600 // nothing to do with it. Then, disconnect the service.
601 if (DEBUG) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900602 Slog.d(TAG, "unbindService(service=" + component + ")");
Jae Seo39570912014-02-20 18:23:25 -0800603 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900604 mContext.unbindService(serviceState.connection);
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +0900605 userState.serviceStateMap.remove(component);
Jae Seo39570912014-02-20 18:23:25 -0800606 }
607 }
608
Dongwon Kang426c9a42014-08-26 17:39:21 +0900609 private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
610 String inputId, int userId) {
611 // Let clients know the create session requests are failed.
Jae Seo4f1a6d42015-07-20 16:15:01 -0700612 UserState userState = getOrCreateUserStateLocked(userId);
Dongwon Kangf7f49dd2014-08-27 20:48:22 +0900613 List<SessionState> sessionsToAbort = new ArrayList<>();
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900614 for (IBinder sessionToken : serviceState.sessionTokens) {
Dongwon Kang426c9a42014-08-26 17:39:21 +0900615 SessionState sessionState = userState.sessionStateMap.get(sessionToken);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900616 if (sessionState.session == null && (inputId == null
Jae Seo2cdb05e2016-02-04 22:17:13 +0900617 || sessionState.inputId.equals(inputId))) {
Dongwon Kangf7f49dd2014-08-27 20:48:22 +0900618 sessionsToAbort.add(sessionState);
Dongwon Kang426c9a42014-08-26 17:39:21 +0900619 }
620 }
Dongwon Kangf7f49dd2014-08-27 20:48:22 +0900621 for (SessionState sessionState : sessionsToAbort) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900622 removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
623 sendSessionTokenToClientLocked(sessionState.client,
Jae Seo2cdb05e2016-02-04 22:17:13 +0900624 sessionState.inputId, null, null, sessionState.seq);
Dongwon Kangf7f49dd2014-08-27 20:48:22 +0900625 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900626 updateServiceConnectionLocked(serviceState.component, userId);
Dongwon Kang426c9a42014-08-26 17:39:21 +0900627 }
628
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900629 private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
630 int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700631 UserState userState = getOrCreateUserStateLocked(userId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900632 SessionState sessionState = userState.sessionStateMap.get(sessionToken);
Jae Seo39570912014-02-20 18:23:25 -0800633 if (DEBUG) {
Jae Seo2cdb05e2016-02-04 22:17:13 +0900634 Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")");
Jae Seo39570912014-02-20 18:23:25 -0800635 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900636 InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
Jae Seo6a6059a2014-04-17 21:35:29 -0700637
Jae Seo39570912014-02-20 18:23:25 -0800638 // Set up a callback to send the session token.
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900639 ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
Jae Seo39570912014-02-20 18:23:25 -0800640
641 // Create a session. When failed, send a null token immediately.
642 try {
Jae Seoa826d012016-01-18 13:03:35 -0800643 if (sessionState.isRecordingSession) {
Jae Seo2cdb05e2016-02-04 22:17:13 +0900644 service.createRecordingSession(callback, sessionState.inputId);
Jae Seoa826d012016-01-18 13:03:35 -0800645 } else {
Jae Seo2cdb05e2016-02-04 22:17:13 +0900646 service.createSession(channels[1], callback, sessionState.inputId);
Jae Seoa826d012016-01-18 13:03:35 -0800647 }
Jae Seo39570912014-02-20 18:23:25 -0800648 } catch (RemoteException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +0900649 Slog.e(TAG, "error in createSession", e);
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900650 removeSessionStateLocked(sessionToken, userId);
Jae Seo2cdb05e2016-02-04 22:17:13 +0900651 sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900652 null, sessionState.seq);
Jae Seo39570912014-02-20 18:23:25 -0800653 }
Jae Seo6a6059a2014-04-17 21:35:29 -0700654 channels[1].dispose();
Jae Seo39570912014-02-20 18:23:25 -0800655 }
656
Sungsoo Limd6672b52014-04-30 10:43:26 +0900657 private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
Jae Seo5c80ad22014-06-12 19:52:58 -0700658 IBinder sessionToken, InputChannel channel, int seq) {
Jae Seo39570912014-02-20 18:23:25 -0800659 try {
Sungsoo Limd6672b52014-04-30 10:43:26 +0900660 client.onSessionCreated(inputId, sessionToken, channel, seq);
Jae Seofea8dd42014-08-26 13:57:41 -0700661 } catch (RemoteException e) {
662 Slog.e(TAG, "error in onSessionCreated", e);
Jae Seo39570912014-02-20 18:23:25 -0800663 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900664 }
Jae Seo39570912014-02-20 18:23:25 -0800665
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900666 private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900667 SessionState sessionState = null;
668 try {
669 sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
670 if (sessionState.session != null) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700671 UserState userState = getOrCreateUserStateLocked(userId);
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900672 if (sessionToken == userState.mainSessionToken) {
673 setMainLocked(sessionToken, false, callingUid, userId);
674 }
Jae Seoe3c11e82016-02-08 23:18:49 -0800675 sessionState.session.release();
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900676 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900677 } catch (RemoteException | SessionNotFoundException e) {
678 Slog.e(TAG, "error in releaseSession", e);
679 } finally {
680 if (sessionState != null) {
681 sessionState.session = null;
682 }
Jae Seo39570912014-02-20 18:23:25 -0800683 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900684 removeSessionStateLocked(sessionToken, userId);
Jae Seo39570912014-02-20 18:23:25 -0800685 }
686
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900687 private void removeSessionStateLocked(IBinder sessionToken, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700688 UserState userState = getOrCreateUserStateLocked(userId);
Ji-Hwan Leeabca0ee2014-07-24 17:34:19 +0900689 if (sessionToken == userState.mainSessionToken) {
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900690 if (DEBUG) {
691 Slog.d(TAG, "mainSessionToken=null");
692 }
Ji-Hwan Leeabca0ee2014-07-24 17:34:19 +0900693 userState.mainSessionToken = null;
694 }
695
696 // Remove the session state from the global session state map of the current user.
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900697 SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
698
Chulwoo Lee8d4ded02014-07-10 03:56:39 +0900699 if (sessionState == null) {
700 return;
701 }
702
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900703 // Also remove the session token from the session token list of the current client and
704 // service.
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900705 ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900706 if (clientState != null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900707 clientState.sessionTokens.remove(sessionToken);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900708 if (clientState.isEmpty()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900709 userState.clientStateMap.remove(sessionState.client.asBinder());
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +0900710 }
711 }
712
Jae Seo2cdb05e2016-02-04 22:17:13 +0900713 ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName);
714 if (serviceState != null) {
715 serviceState.sessionTokens.remove(sessionToken);
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900716 }
Jae Seo2cdb05e2016-02-04 22:17:13 +0900717 updateServiceConnectionLocked(sessionState.componentName, userId);
Jae Seo7eb75df2014-08-08 22:20:48 -0700718
719 // Log the end of watch.
720 SomeArgs args = SomeArgs.obtain();
721 args.arg1 = sessionToken;
722 args.arg2 = System.currentTimeMillis();
723 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget();
Dongwon Kangfd5b72f2014-04-15 17:23:24 +0900724 }
725
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900726 private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900727 try {
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900728 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
729 if (sessionState.hardwareSessionToken != null) {
730 sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
731 Process.SYSTEM_UID, userId);
732 }
Jae Seo2cdb05e2016-02-04 22:17:13 +0900733 ServiceState serviceState = getServiceStateLocked(sessionState.componentName, userId);
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900734 if (!serviceState.isHardware) {
735 return;
736 }
737 ITvInputSession session = getSessionLocked(sessionState);
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900738 session.setMain(isMain);
Dongwon Kangfdce9e52014-12-04 18:08:00 +0900739 } catch (RemoteException | SessionNotFoundException e) {
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +0900740 Slog.e(TAG, "error in setMain", e);
741 }
742 }
743
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900744 private void notifyInputAddedLocked(UserState userState, String inputId) {
745 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -0700746 Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")");
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900747 }
748 for (ITvInputManagerCallback callback : userState.callbackSet) {
749 try {
750 callback.onInputAdded(inputId);
751 } catch (RemoteException e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700752 Slog.e(TAG, "failed to report added input to callback", e);
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900753 }
754 }
755 }
756
757 private void notifyInputRemovedLocked(UserState userState, String inputId) {
758 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -0700759 Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")");
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900760 }
761 for (ITvInputManagerCallback callback : userState.callbackSet) {
762 try {
763 callback.onInputRemoved(inputId);
764 } catch (RemoteException e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700765 Slog.e(TAG, "failed to report removed input to callback", e);
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900766 }
767 }
768 }
769
Chulwoo Lee19ba61a2014-09-03 00:59:35 +0900770 private void notifyInputUpdatedLocked(UserState userState, String inputId) {
771 if (DEBUG) {
772 Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")");
773 }
774 for (ITvInputManagerCallback callback : userState.callbackSet) {
775 try {
776 callback.onInputUpdated(inputId);
777 } catch (RemoteException e) {
778 Slog.e(TAG, "failed to report updated input to callback", e);
779 }
780 }
781 }
782
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900783 private void notifyInputStateChangedLocked(UserState userState, String inputId,
Wonsik Kim969167d2014-06-24 16:33:17 +0900784 int state, ITvInputManagerCallback targetCallback) {
785 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -0700786 Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId
787 + ", state=" + state + ")");
Wonsik Kim969167d2014-06-24 16:33:17 +0900788 }
789 if (targetCallback == null) {
790 for (ITvInputManagerCallback callback : userState.callbackSet) {
791 try {
792 callback.onInputStateChanged(inputId, state);
793 } catch (RemoteException e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700794 Slog.e(TAG, "failed to report state change to callback", e);
Wonsik Kim969167d2014-06-24 16:33:17 +0900795 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900796 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900797 } else {
798 try {
799 targetCallback.onInputStateChanged(inputId, state);
800 } catch (RemoteException e) {
Jae Seofea8dd42014-08-26 13:57:41 -0700801 Slog.e(TAG, "failed to report state change to callback", e);
Wonsik Kim969167d2014-06-24 16:33:17 +0900802 }
803 }
804 }
805
Jae Seoaa5605f2016-02-13 01:38:08 -0800806 private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) {
Jae Seoa826d012016-01-18 13:03:35 -0800807 if (DEBUG) {
Jae Seoaa5605f2016-02-13 01:38:08 -0800808 Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")");
Jae Seoa826d012016-01-18 13:03:35 -0800809 }
Jae Seoabda4202016-01-28 19:13:04 -0800810 String inputId = inputInfo.getId();
811 TvInputState inputState = userState.inputMap.get(inputId);
812 if (inputState == null) {
813 Slog.e(TAG, "failed to set input info - unknown input id " + inputId);
814 return;
815 }
Jae Seoabda4202016-01-28 19:13:04 -0800816 inputState.info = inputInfo;
817
Jae Seoa826d012016-01-18 13:03:35 -0800818 for (ITvInputManagerCallback callback : userState.callbackSet) {
819 try {
Jae Seoaa5605f2016-02-13 01:38:08 -0800820 callback.onTvInputInfoUpdated(inputInfo);
Jae Seoa826d012016-01-18 13:03:35 -0800821 } catch (RemoteException e) {
Jae Seoaa5605f2016-02-13 01:38:08 -0800822 Slog.e(TAG, "failed to report updated input info to callback", e);
Jae Seoa826d012016-01-18 13:03:35 -0800823 }
824 }
825 }
826
Wonsik Kim969167d2014-06-24 16:33:17 +0900827 private void setStateLocked(String inputId, int state, int userId) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700828 UserState userState = getOrCreateUserStateLocked(userId);
Wonsik Kim969167d2014-06-24 16:33:17 +0900829 TvInputState inputState = userState.inputMap.get(inputId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900830 ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
831 int oldState = inputState.state;
832 inputState.state = state;
833 if (serviceState != null && serviceState.service == null
shubang8049f202016-04-25 11:21:42 -0700834 && (!serviceState.sessionTokens.isEmpty() || serviceState.isHardware)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900835 // We don't notify state change while reconnecting. It should remain disconnected.
836 return;
837 }
838 if (oldState != state) {
Jaewan Kim8e6b51b2014-07-15 13:01:57 +0900839 notifyInputStateChangedLocked(userState, inputId, state, null);
Sungsoo Lim2b35a722014-04-17 17:09:15 +0900840 }
841 }
842
Jae Seo39570912014-02-20 18:23:25 -0800843 private final class BinderService extends ITvInputManager.Stub {
844 @Override
845 public List<TvInputInfo> getTvInputList(int userId) {
846 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
847 Binder.getCallingUid(), userId, "getTvInputList");
848 final long identity = Binder.clearCallingIdentity();
849 try {
850 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700851 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700852 List<TvInputInfo> inputList = new ArrayList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900853 for (TvInputState state : userState.inputMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900854 inputList.add(state.info);
Jae Seo39570912014-02-20 18:23:25 -0800855 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900856 return inputList;
Jae Seo39570912014-02-20 18:23:25 -0800857 }
858 } finally {
859 Binder.restoreCallingIdentity(identity);
860 }
Jae Seo39570912014-02-20 18:23:25 -0800861 }
862
863 @Override
Jae Seob3758052014-07-12 19:25:24 -0700864 public TvInputInfo getTvInputInfo(String inputId, int userId) {
865 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
866 Binder.getCallingUid(), userId, "getTvInputInfo");
867 final long identity = Binder.clearCallingIdentity();
868 try {
869 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700870 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seob3758052014-07-12 19:25:24 -0700871 TvInputState state = userState.inputMap.get(inputId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +0900872 return state == null ? null : state.info;
Jae Seob3758052014-07-12 19:25:24 -0700873 }
874 } finally {
875 Binder.restoreCallingIdentity(identity);
876 }
877 }
878
Jae Seoaa5605f2016-02-13 01:38:08 -0800879 public void updateTvInputInfo(TvInputInfo inputInfo, int userId) {
Jae Seoc2a89512016-01-28 10:38:11 -0800880 String inputInfoPackageName = inputInfo.getServiceInfo().packageName;
881 String callingPackageName = getCallingPackageName();
Dongwon Kang9d0e0f12016-10-11 15:14:01 -0700882 if (!TextUtils.equals(inputInfoPackageName, callingPackageName)
883 && mContext.checkCallingPermission(
884 android.Manifest.permission.WRITE_SECURE_SETTINGS)
885 != PackageManager.PERMISSION_GRANTED) {
886 // Only the app owning the input and system settings are allowed to update info.
Jae Seoc2a89512016-01-28 10:38:11 -0800887 throw new IllegalArgumentException("calling package " + callingPackageName
888 + " is not allowed to change TvInputInfo for " + inputInfoPackageName);
889 }
890
891 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Jae Seoaa5605f2016-02-13 01:38:08 -0800892 Binder.getCallingUid(), userId, "updateTvInputInfo");
Jae Seoc2a89512016-01-28 10:38:11 -0800893 final long identity = Binder.clearCallingIdentity();
894 try {
895 synchronized (mLock) {
896 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seoaa5605f2016-02-13 01:38:08 -0800897 updateTvInputInfoLocked(userState, inputInfo);
Jae Seoc2a89512016-01-28 10:38:11 -0800898 }
899 } finally {
900 Binder.restoreCallingIdentity(identity);
901 }
902 }
903
904 private String getCallingPackageName() {
905 final String[] packages = mContext.getPackageManager().getPackagesForUid(
906 Binder.getCallingUid());
907 if (packages != null && packages.length > 0) {
908 return packages[0];
909 }
910 return "unknown";
911 }
912
Jae Seob3758052014-07-12 19:25:24 -0700913 @Override
Dongwon Kang993f81e2014-11-27 19:34:18 +0900914 public int getTvInputState(String inputId, int userId) {
915 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
916 Binder.getCallingUid(), userId, "getTvInputState");
917 final long identity = Binder.clearCallingIdentity();
918 try {
919 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700920 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Dongwon Kang993f81e2014-11-27 19:34:18 +0900921 TvInputState state = userState.inputMap.get(inputId);
Jae Seo82fce642015-04-20 15:37:50 -0700922 return state == null ? INPUT_STATE_CONNECTED : state.state;
Dongwon Kang993f81e2014-11-27 19:34:18 +0900923 }
924 } finally {
925 Binder.restoreCallingIdentity(identity);
926 }
927 }
928
929 @Override
Jae Seo9c165d62014-08-25 14:39:26 -0700930 public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) {
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900931 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Jae Seo9c165d62014-08-25 14:39:26 -0700932 Binder.getCallingUid(), userId, "getTvContentRatingSystemList");
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900933 final long identity = Binder.clearCallingIdentity();
934 try {
935 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700936 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo9c165d62014-08-25 14:39:26 -0700937 return userState.contentRatingSystemList;
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +0900938 }
939 } finally {
940 Binder.restoreCallingIdentity(identity);
941 }
942 }
943
944 @Override
Conrad Chen558acf92017-04-05 17:31:01 -0700945 public void sendTvInputNotifyIntent(Intent intent, int userId) {
946 if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
947 != PackageManager.PERMISSION_GRANTED) {
948 throw new SecurityException("The caller: " + getCallingPackageName()
949 + " doesn't have permission: "
950 + android.Manifest.permission.NOTIFY_TV_INPUTS);
951 }
952 if (TextUtils.isEmpty(intent.getPackage())) {
953 throw new IllegalArgumentException("Must specify package name to notify.");
954 }
955 switch (intent.getAction()) {
956 case TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
957 if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
958 throw new IllegalArgumentException("Invalid preview program ID.");
959 }
960 break;
961 case TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
962 if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
963 throw new IllegalArgumentException("Invalid watch next program ID.");
964 }
965 break;
966 case TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
967 if (intent.getLongExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, -1) < 0) {
968 throw new IllegalArgumentException("Invalid preview program ID.");
969 }
970 if (intent.getLongExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, -1) < 0) {
971 throw new IllegalArgumentException("Invalid watch next program ID.");
972 }
973 break;
974 default:
975 throw new IllegalArgumentException("Invalid TV input notifying action: "
976 + intent.getAction());
977 }
978 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
979 Binder.getCallingUid(), userId, "sendTvInputNotifyIntent");
980 final long identity = Binder.clearCallingIdentity();
981 try {
982 getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
983 } finally {
984 Binder.restoreCallingIdentity(identity);
985 }
986 }
987
988 @Override
Wonsik Kim969167d2014-06-24 16:33:17 +0900989 public void registerCallback(final ITvInputManagerCallback callback, int userId) {
Jae Seo39570912014-02-20 18:23:25 -0800990 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
991 Binder.getCallingUid(), userId, "registerCallback");
992 final long identity = Binder.clearCallingIdentity();
993 try {
994 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -0700995 final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Wonsik Kim969167d2014-06-24 16:33:17 +0900996 userState.callbackSet.add(callback);
Jae Seofea8dd42014-08-26 13:57:41 -0700997 try {
998 callback.asBinder().linkToDeath(new IBinder.DeathRecipient() {
999 @Override
1000 public void binderDied() {
1001 synchronized (mLock) {
1002 if (userState.callbackSet != null) {
1003 userState.callbackSet.remove(callback);
1004 }
1005 }
1006 }
1007 }, 0);
1008 } catch (RemoteException e) {
1009 Slog.e(TAG, "client process has already died", e);
1010 }
Jae Seo39570912014-02-20 18:23:25 -08001011 }
1012 } finally {
1013 Binder.restoreCallingIdentity(identity);
1014 }
1015 }
1016
1017 @Override
Wonsik Kim969167d2014-06-24 16:33:17 +09001018 public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
Jae Seo39570912014-02-20 18:23:25 -08001019 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1020 Binder.getCallingUid(), userId, "unregisterCallback");
1021 final long identity = Binder.clearCallingIdentity();
1022 try {
1023 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001024 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Wonsik Kim969167d2014-06-24 16:33:17 +09001025 userState.callbackSet.remove(callback);
Jae Seo39570912014-02-20 18:23:25 -08001026 }
1027 } finally {
1028 Binder.restoreCallingIdentity(identity);
1029 }
1030 }
1031
1032 @Override
Jae Seo783645e2014-07-28 17:30:50 +09001033 public boolean isParentalControlsEnabled(int userId) {
1034 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1035 Binder.getCallingUid(), userId, "isParentalControlsEnabled");
1036 final long identity = Binder.clearCallingIdentity();
1037 try {
1038 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001039 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001040 return userState.persistentDataStore.isParentalControlsEnabled();
1041 }
1042 } finally {
1043 Binder.restoreCallingIdentity(identity);
1044 }
1045 }
1046
1047 @Override
1048 public void setParentalControlsEnabled(boolean enabled, int userId) {
1049 ensureParentalControlsPermission();
1050 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1051 Binder.getCallingUid(), userId, "setParentalControlsEnabled");
1052 final long identity = Binder.clearCallingIdentity();
1053 try {
1054 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001055 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001056 userState.persistentDataStore.setParentalControlsEnabled(enabled);
1057 }
1058 } finally {
1059 Binder.restoreCallingIdentity(identity);
1060 }
1061 }
1062
1063 @Override
1064 public boolean isRatingBlocked(String rating, int userId) {
1065 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1066 Binder.getCallingUid(), userId, "isRatingBlocked");
1067 final long identity = Binder.clearCallingIdentity();
1068 try {
1069 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001070 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001071 return userState.persistentDataStore.isRatingBlocked(
1072 TvContentRating.unflattenFromString(rating));
1073 }
1074 } finally {
1075 Binder.restoreCallingIdentity(identity);
1076 }
1077 }
1078
1079 @Override
1080 public List<String> getBlockedRatings(int userId) {
1081 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1082 Binder.getCallingUid(), userId, "getBlockedRatings");
1083 final long identity = Binder.clearCallingIdentity();
1084 try {
1085 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001086 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001087 List<String> ratings = new ArrayList<>();
Jae Seo783645e2014-07-28 17:30:50 +09001088 for (TvContentRating rating
1089 : userState.persistentDataStore.getBlockedRatings()) {
1090 ratings.add(rating.flattenToString());
1091 }
1092 return ratings;
1093 }
1094 } finally {
1095 Binder.restoreCallingIdentity(identity);
1096 }
1097 }
1098
1099 @Override
1100 public void addBlockedRating(String rating, int userId) {
1101 ensureParentalControlsPermission();
1102 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1103 Binder.getCallingUid(), userId, "addBlockedRating");
1104 final long identity = Binder.clearCallingIdentity();
1105 try {
1106 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001107 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001108 userState.persistentDataStore.addBlockedRating(
1109 TvContentRating.unflattenFromString(rating));
1110 }
1111 } finally {
1112 Binder.restoreCallingIdentity(identity);
1113 }
1114 }
1115
1116 @Override
1117 public void removeBlockedRating(String rating, int userId) {
1118 ensureParentalControlsPermission();
1119 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1120 Binder.getCallingUid(), userId, "removeBlockedRating");
1121 final long identity = Binder.clearCallingIdentity();
1122 try {
1123 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001124 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo783645e2014-07-28 17:30:50 +09001125 userState.persistentDataStore.removeBlockedRating(
1126 TvContentRating.unflattenFromString(rating));
1127 }
1128 } finally {
1129 Binder.restoreCallingIdentity(identity);
1130 }
1131 }
1132
1133 private void ensureParentalControlsPermission() {
Jae Seofc836f62014-08-27 00:47:56 +00001134 if (mContext.checkCallingPermission(
1135 android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
1136 != PackageManager.PERMISSION_GRANTED) {
1137 throw new SecurityException(
1138 "The caller does not have parental controls permission");
1139 }
Jae Seo783645e2014-07-28 17:30:50 +09001140 }
1141
1142 @Override
Sungsoo Limd6672b52014-04-30 10:43:26 +09001143 public void createSession(final ITvInputClient client, final String inputId,
Jae Seoa826d012016-01-18 13:03:35 -08001144 boolean isRecordingSession, int seq, int userId) {
Jae Seo39570912014-02-20 18:23:25 -08001145 final int callingUid = Binder.getCallingUid();
1146 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1147 userId, "createSession");
1148 final long identity = Binder.clearCallingIdentity();
1149 try {
1150 synchronized (mLock) {
shubang8049f202016-04-25 11:21:42 -07001151 if (userId != mCurrentUserId && !isRecordingSession) {
1152 // A non-recording session of a backgroud (non-current) user
1153 // should not be created.
1154 // Let the client get onConnectionFailed callback for this case.
1155 sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1156 return;
1157 }
Jae Seo4f1a6d42015-07-20 16:15:01 -07001158 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Dongwon Kang426c9a42014-08-26 17:39:21 +09001159 TvInputState inputState = userState.inputMap.get(inputId);
1160 if (inputState == null) {
1161 Slog.w(TAG, "Failed to find input state for inputId=" + inputId);
1162 sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1163 return;
1164 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001165 TvInputInfo info = inputState.info;
Wonsik Kim187423c2014-06-25 14:12:48 +09001166 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
Jae Seo39570912014-02-20 18:23:25 -08001167 if (serviceState == null) {
Wonsik Kim187423c2014-06-25 14:12:48 +09001168 serviceState = new ServiceState(info.getComponent(), resolvedUserId);
1169 userState.serviceStateMap.put(info.getComponent(), serviceState);
Jae Seo39570912014-02-20 18:23:25 -08001170 }
Sungsoo Lim2b35a722014-04-17 17:09:15 +09001171 // Send a null token immediately while reconnecting.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001172 if (serviceState.reconnecting) {
Jae Seo5c80ad22014-06-12 19:52:58 -07001173 sendSessionTokenToClientLocked(client, inputId, null, null, seq);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09001174 return;
1175 }
1176
1177 // Create a new session token and a session state.
1178 IBinder sessionToken = new Binder();
Jae Seo2cdb05e2016-02-04 22:17:13 +09001179 SessionState sessionState = new SessionState(sessionToken, info.getId(),
1180 info.getComponent(), isRecordingSession, client, seq, callingUid,
1181 resolvedUserId);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09001182
1183 // Add them to the global session state map of the current user.
1184 userState.sessionStateMap.put(sessionToken, sessionState);
1185
1186 // Also, add them to the session state map of the current service.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001187 serviceState.sessionTokens.add(sessionToken);
Jae Seo39570912014-02-20 18:23:25 -08001188
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001189 if (serviceState.service != null) {
1190 createSessionInternalLocked(serviceState.service, sessionToken,
Sungsoo Lim7de5e232014-04-12 16:51:27 +09001191 resolvedUserId);
Jae Seo39570912014-02-20 18:23:25 -08001192 } else {
Wonsik Kim187423c2014-06-25 14:12:48 +09001193 updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
Jae Seo39570912014-02-20 18:23:25 -08001194 }
1195 }
1196 } finally {
1197 Binder.restoreCallingIdentity(identity);
1198 }
1199 }
1200
1201 @Override
1202 public void releaseSession(IBinder sessionToken, int userId) {
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001203 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -07001204 Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001205 }
Jae Seo39570912014-02-20 18:23:25 -08001206 final int callingUid = Binder.getCallingUid();
1207 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1208 userId, "releaseSession");
1209 final long identity = Binder.clearCallingIdentity();
1210 try {
1211 synchronized (mLock) {
Sungsoo Lim2b35a722014-04-17 17:09:15 +09001212 releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
Jae Seo39570912014-02-20 18:23:25 -08001213 }
1214 } finally {
1215 Binder.restoreCallingIdentity(identity);
1216 }
1217 }
1218
1219 @Override
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001220 public void setMainSession(IBinder sessionToken, int userId) {
Shubange41b76f2017-06-07 13:23:12 -07001221 if (mContext.checkCallingPermission(
1222 android.Manifest.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE)
1223 != PackageManager.PERMISSION_GRANTED) {
1224 throw new SecurityException(
1225 "The caller does not have CHANGE_HDMI_CEC_ACTIVE_SOURCE permission");
1226 }
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001227 if (DEBUG) {
Jae Seofea8dd42014-08-26 13:57:41 -07001228 Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")");
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001229 }
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001230 final int callingUid = Binder.getCallingUid();
1231 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1232 userId, "setMainSession");
1233 final long identity = Binder.clearCallingIdentity();
1234 try {
1235 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001236 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Ji-Hwan Lee956afc22014-07-26 11:31:39 +09001237 if (userState.mainSessionToken == sessionToken) {
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001238 return;
1239 }
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001240 if (DEBUG) {
1241 Slog.d(TAG, "mainSessionToken=" + sessionToken);
Ji-Hwan Lee956afc22014-07-26 11:31:39 +09001242 }
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001243 IBinder oldMainSessionToken = userState.mainSessionToken;
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001244 userState.mainSessionToken = sessionToken;
1245
Ji-Hwan Lee956afc22014-07-26 11:31:39 +09001246 // Inform the new main session first.
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001247 // See {@link TvInputService.Session#onSetMain}.
1248 if (sessionToken != null) {
1249 setMainLocked(sessionToken, true, callingUid, userId);
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001250 }
Ji-Hwan Lee15c56aa2014-08-18 22:01:55 +09001251 if (oldMainSessionToken != null) {
1252 setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId);
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09001253 }
1254 }
1255 } finally {
1256 Binder.restoreCallingIdentity(identity);
1257 }
1258 }
1259
1260 @Override
Jae Seo39570912014-02-20 18:23:25 -08001261 public void setSurface(IBinder sessionToken, Surface surface, int userId) {
1262 final int callingUid = Binder.getCallingUid();
1263 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1264 userId, "setSurface");
1265 final long identity = Binder.clearCallingIdentity();
1266 try {
1267 synchronized (mLock) {
1268 try {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001269 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1270 resolvedUserId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001271 if (sessionState.hardwareSessionToken == null) {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001272 getSessionLocked(sessionState).setSurface(surface);
1273 } else {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001274 getSessionLocked(sessionState.hardwareSessionToken,
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001275 Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
1276 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001277 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001278 Slog.e(TAG, "error in setSurface", e);
Jae Seo39570912014-02-20 18:23:25 -08001279 }
1280 }
1281 } finally {
Youngsang Chof8362062014-04-30 17:24:20 +09001282 if (surface != null) {
1283 // surface is not used in TvInputManagerService.
1284 surface.release();
1285 }
Jae Seo39570912014-02-20 18:23:25 -08001286 Binder.restoreCallingIdentity(identity);
1287 }
1288 }
1289
1290 @Override
Youngsang Choe821d712014-07-16 14:22:19 -07001291 public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
1292 int height, int userId) {
1293 final int callingUid = Binder.getCallingUid();
1294 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1295 userId, "dispatchSurfaceChanged");
1296 final long identity = Binder.clearCallingIdentity();
1297 try {
1298 synchronized (mLock) {
1299 try {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001300 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1301 resolvedUserId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001302 getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
1303 height);
1304 if (sessionState.hardwareSessionToken != null) {
1305 getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001306 resolvedUserId).dispatchSurfaceChanged(format, width, height);
1307 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001308 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Choe821d712014-07-16 14:22:19 -07001309 Slog.e(TAG, "error in dispatchSurfaceChanged", e);
1310 }
1311 }
1312 } finally {
1313 Binder.restoreCallingIdentity(identity);
1314 }
1315 }
1316
1317 @Override
Jae Seo39570912014-02-20 18:23:25 -08001318 public void setVolume(IBinder sessionToken, float volume, int userId) {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001319 final float REMOTE_VOLUME_ON = 1.0f;
1320 final float REMOTE_VOLUME_OFF = 0f;
Jae Seo39570912014-02-20 18:23:25 -08001321 final int callingUid = Binder.getCallingUid();
1322 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1323 userId, "setVolume");
1324 final long identity = Binder.clearCallingIdentity();
1325 try {
1326 synchronized (mLock) {
1327 try {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001328 SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1329 resolvedUserId);
1330 getSessionLocked(sessionState).setVolume(volume);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001331 if (sessionState.hardwareSessionToken != null) {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001332 // Here, we let the hardware session know only whether volume is on or
1333 // off to prevent that the volume is controlled in the both side.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001334 getSessionLocked(sessionState.hardwareSessionToken,
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001335 Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
1336 ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
1337 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001338 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001339 Slog.e(TAG, "error in setVolume", e);
Jae Seo39570912014-02-20 18:23:25 -08001340 }
1341 }
1342 } finally {
1343 Binder.restoreCallingIdentity(identity);
1344 }
1345 }
1346
1347 @Override
Sungsoo Lim1a6b25e2014-07-09 10:40:43 +09001348 public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
Jae Seo39570912014-02-20 18:23:25 -08001349 final int callingUid = Binder.getCallingUid();
1350 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1351 userId, "tune");
1352 final long identity = Binder.clearCallingIdentity();
1353 try {
1354 synchronized (mLock) {
Jae Seo39570912014-02-20 18:23:25 -08001355 try {
Sungsoo Lim1a6b25e2014-07-09 10:40:43 +09001356 getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
1357 channelUri, params);
Jae Seoc22d0c02014-08-15 13:03:21 -07001358 if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
Youngsang Cho008f6d42014-07-22 21:29:47 -07001359 // Do not log the watch history for passthrough inputs.
1360 return;
1361 }
Jae Seo31dc634be2014-04-15 17:40:23 -07001362
Jae Seo4f1a6d42015-07-20 16:15:01 -07001363 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Jae Seo31dc634be2014-04-15 17:40:23 -07001364 SessionState sessionState = userState.sessionStateMap.get(sessionToken);
Jae Seoe3c11e82016-02-08 23:18:49 -08001365 if (sessionState.isRecordingSession) {
1366 return;
1367 }
Jae Seo31dc634be2014-04-15 17:40:23 -07001368
Jae Seo7eb75df2014-08-08 22:20:48 -07001369 // Log the start of watch.
Jae Seo31dc634be2014-04-15 17:40:23 -07001370 SomeArgs args = SomeArgs.obtain();
Jae Seo2cdb05e2016-02-04 22:17:13 +09001371 args.arg1 = sessionState.componentName.getPackageName();
Jae Seo7eb75df2014-08-08 22:20:48 -07001372 args.arg2 = System.currentTimeMillis();
1373 args.arg3 = ContentUris.parseId(channelUri);
1374 args.arg4 = params;
1375 args.arg5 = sessionToken;
1376 mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args)
1377 .sendToTarget();
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001378 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001379 Slog.e(TAG, "error in tune", e);
Jae Seo39570912014-02-20 18:23:25 -08001380 }
1381 }
1382 } finally {
1383 Binder.restoreCallingIdentity(identity);
1384 }
1385 }
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001386
1387 @Override
Jae Seoa9033832015-03-11 19:29:46 -07001388 public void unblockContent(
Sungsoo Lim9bf671f2014-07-19 12:59:51 +09001389 IBinder sessionToken, String unblockedRating, int userId) {
Dongwon Kange12d8102016-03-04 16:45:39 -08001390 ensureParentalControlsPermission();
Jaewan Kim903d6b72014-07-16 11:28:56 +09001391 final int callingUid = Binder.getCallingUid();
1392 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1393 userId, "unblockContent");
1394 final long identity = Binder.clearCallingIdentity();
1395 try {
1396 synchronized (mLock) {
1397 try {
1398 getSessionLocked(sessionToken, callingUid, resolvedUserId)
Jae Seoa9033832015-03-11 19:29:46 -07001399 .unblockContent(unblockedRating);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001400 } catch (RemoteException | SessionNotFoundException e) {
Jae Seoa9033832015-03-11 19:29:46 -07001401 Slog.e(TAG, "error in unblockContent", e);
Jaewan Kim903d6b72014-07-16 11:28:56 +09001402 }
1403 }
1404 } finally {
1405 Binder.restoreCallingIdentity(identity);
1406 }
1407 }
1408
1409 @Override
Jae Seo2c1c31c2014-07-10 14:57:01 -07001410 public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
1411 final int callingUid = Binder.getCallingUid();
1412 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1413 userId, "setCaptionEnabled");
1414 final long identity = Binder.clearCallingIdentity();
1415 try {
1416 synchronized (mLock) {
1417 try {
1418 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1419 .setCaptionEnabled(enabled);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001420 } catch (RemoteException | SessionNotFoundException e) {
Jae Seo2c1c31c2014-07-10 14:57:01 -07001421 Slog.e(TAG, "error in setCaptionEnabled", e);
1422 }
1423 }
1424 } finally {
1425 Binder.restoreCallingIdentity(identity);
1426 }
1427 }
1428
1429 @Override
Jae Seo10d285a2014-07-31 22:46:47 +09001430 public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
Dongwon Kang1f213912014-07-02 18:35:08 +09001431 final int callingUid = Binder.getCallingUid();
1432 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1433 userId, "selectTrack");
1434 final long identity = Binder.clearCallingIdentity();
1435 try {
1436 synchronized (mLock) {
1437 try {
1438 getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
Jae Seo10d285a2014-07-31 22:46:47 +09001439 type, trackId);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001440 } catch (RemoteException | SessionNotFoundException e) {
Dongwon Kang1f213912014-07-02 18:35:08 +09001441 Slog.e(TAG, "error in selectTrack", e);
1442 }
1443 }
1444 } finally {
1445 Binder.restoreCallingIdentity(identity);
1446 }
1447 }
1448
1449 @Override
Jae Seoa759b112014-07-18 22:16:08 -07001450 public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
1451 int userId) {
1452 final int callingUid = Binder.getCallingUid();
1453 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1454 userId, "sendAppPrivateCommand");
1455 final long identity = Binder.clearCallingIdentity();
1456 try {
1457 synchronized (mLock) {
1458 try {
1459 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1460 .appPrivateCommand(command, data);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001461 } catch (RemoteException | SessionNotFoundException e) {
Jae Seofea8dd42014-08-26 13:57:41 -07001462 Slog.e(TAG, "error in appPrivateCommand", e);
Jae Seoa759b112014-07-18 22:16:08 -07001463 }
1464 }
1465 } finally {
1466 Binder.restoreCallingIdentity(identity);
1467 }
1468 }
1469
1470 @Override
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001471 public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
1472 int userId) {
1473 final int callingUid = Binder.getCallingUid();
1474 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1475 userId, "createOverlayView");
1476 final long identity = Binder.clearCallingIdentity();
1477 try {
1478 synchronized (mLock) {
1479 try {
1480 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1481 .createOverlayView(windowToken, frame);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001482 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001483 Slog.e(TAG, "error in createOverlayView", e);
1484 }
1485 }
1486 } finally {
1487 Binder.restoreCallingIdentity(identity);
1488 }
1489 }
1490
1491 @Override
1492 public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
1493 final int callingUid = Binder.getCallingUid();
1494 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1495 userId, "relayoutOverlayView");
1496 final long identity = Binder.clearCallingIdentity();
1497 try {
1498 synchronized (mLock) {
1499 try {
1500 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1501 .relayoutOverlayView(frame);
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001502 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001503 Slog.e(TAG, "error in relayoutOverlayView", e);
1504 }
1505 }
1506 } finally {
1507 Binder.restoreCallingIdentity(identity);
1508 }
1509 }
1510
1511 @Override
1512 public void removeOverlayView(IBinder sessionToken, int userId) {
1513 final int callingUid = Binder.getCallingUid();
1514 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1515 userId, "removeOverlayView");
1516 final long identity = Binder.clearCallingIdentity();
1517 try {
1518 synchronized (mLock) {
1519 try {
1520 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1521 .removeOverlayView();
Dongwon Kangfdce9e52014-12-04 18:08:00 +09001522 } catch (RemoteException | SessionNotFoundException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09001523 Slog.e(TAG, "error in removeOverlayView", e);
1524 }
1525 }
1526 } finally {
1527 Binder.restoreCallingIdentity(identity);
1528 }
1529 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001530
1531 @Override
Jae Seoa826d012016-01-18 13:03:35 -08001532 public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
1533 final int callingUid = Binder.getCallingUid();
1534 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1535 userId, "timeShiftPlay");
1536 final long identity = Binder.clearCallingIdentity();
1537 try {
1538 synchronized (mLock) {
1539 try {
1540 getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay(
1541 recordedProgramUri);
1542 } catch (RemoteException | SessionNotFoundException e) {
1543 Slog.e(TAG, "error in timeShiftPlay", e);
1544 }
1545 }
1546 } finally {
1547 Binder.restoreCallingIdentity(identity);
1548 }
1549 }
1550
1551 @Override
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001552 public void timeShiftPause(IBinder sessionToken, int userId) {
1553 final int callingUid = Binder.getCallingUid();
1554 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1555 userId, "timeShiftPause");
1556 final long identity = Binder.clearCallingIdentity();
1557 try {
1558 synchronized (mLock) {
1559 try {
Jae Seoa826d012016-01-18 13:03:35 -08001560 getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause();
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001561 } catch (RemoteException | SessionNotFoundException e) {
1562 Slog.e(TAG, "error in timeShiftPause", e);
1563 }
1564 }
1565 } finally {
1566 Binder.restoreCallingIdentity(identity);
1567 }
1568 }
1569
1570 @Override
1571 public void timeShiftResume(IBinder sessionToken, int userId) {
1572 final int callingUid = Binder.getCallingUid();
1573 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1574 userId, "timeShiftResume");
1575 final long identity = Binder.clearCallingIdentity();
1576 try {
1577 synchronized (mLock) {
1578 try {
1579 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1580 .timeShiftResume();
1581 } catch (RemoteException | SessionNotFoundException e) {
1582 Slog.e(TAG, "error in timeShiftResume", e);
1583 }
1584 }
1585 } finally {
1586 Binder.restoreCallingIdentity(identity);
1587 }
1588 }
1589
1590 @Override
1591 public void timeShiftSeekTo(IBinder sessionToken, long timeMs, int userId) {
1592 final int callingUid = Binder.getCallingUid();
1593 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1594 userId, "timeShiftSeekTo");
1595 final long identity = Binder.clearCallingIdentity();
1596 try {
1597 synchronized (mLock) {
1598 try {
1599 getSessionLocked(sessionToken, callingUid, resolvedUserId)
1600 .timeShiftSeekTo(timeMs);
1601 } catch (RemoteException | SessionNotFoundException e) {
1602 Slog.e(TAG, "error in timeShiftSeekTo", e);
1603 }
1604 }
1605 } finally {
1606 Binder.restoreCallingIdentity(identity);
1607 }
1608 }
1609
1610 @Override
Jae Seo4b34cc72015-05-15 17:29:39 -07001611 public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params,
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001612 int userId) {
1613 final int callingUid = Binder.getCallingUid();
1614 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
Jae Seo4b34cc72015-05-15 17:29:39 -07001615 userId, "timeShiftSetPlaybackParams");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001616 final long identity = Binder.clearCallingIdentity();
1617 try {
1618 synchronized (mLock) {
1619 try {
1620 getSessionLocked(sessionToken, callingUid, resolvedUserId)
Jae Seo4b34cc72015-05-15 17:29:39 -07001621 .timeShiftSetPlaybackParams(params);
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001622 } catch (RemoteException | SessionNotFoundException e) {
Jae Seo4b34cc72015-05-15 17:29:39 -07001623 Slog.e(TAG, "error in timeShiftSetPlaybackParams", e);
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001624 }
1625 }
1626 } finally {
1627 Binder.restoreCallingIdentity(identity);
1628 }
1629 }
1630
1631 @Override
Jae Seo465f0d62015-04-06 18:40:46 -07001632 public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable,
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001633 int userId) {
1634 final int callingUid = Binder.getCallingUid();
1635 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
Jae Seo465f0d62015-04-06 18:40:46 -07001636 userId, "timeShiftEnablePositionTracking");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001637 final long identity = Binder.clearCallingIdentity();
1638 try {
1639 synchronized (mLock) {
1640 try {
1641 getSessionLocked(sessionToken, callingUid, resolvedUserId)
Jae Seo465f0d62015-04-06 18:40:46 -07001642 .timeShiftEnablePositionTracking(enable);
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001643 } catch (RemoteException | SessionNotFoundException e) {
Jae Seo465f0d62015-04-06 18:40:46 -07001644 Slog.e(TAG, "error in timeShiftEnablePositionTracking", e);
Dongwon Kang6f0240c2015-03-31 17:56:36 -07001645 }
1646 }
1647 } finally {
1648 Binder.restoreCallingIdentity(identity);
1649 }
1650 }
1651
1652 @Override
Dongwon Kang0cb52442016-04-22 11:00:03 -07001653 public void startRecording(IBinder sessionToken, @Nullable Uri programUri, int userId) {
Jae Seoa826d012016-01-18 13:03:35 -08001654 final int callingUid = Binder.getCallingUid();
1655 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1656 userId, "startRecording");
1657 final long identity = Binder.clearCallingIdentity();
1658 try {
1659 synchronized (mLock) {
1660 try {
Jae Seo4eee6a72016-02-06 11:11:35 +09001661 getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording(
Dongwon Kang0cb52442016-04-22 11:00:03 -07001662 programUri);
Jae Seoa826d012016-01-18 13:03:35 -08001663 } catch (RemoteException | SessionNotFoundException e) {
1664 Slog.e(TAG, "error in startRecording", e);
1665 }
1666 }
1667 } finally {
1668 Binder.restoreCallingIdentity(identity);
1669 }
1670 }
1671
1672 @Override
1673 public void stopRecording(IBinder sessionToken, int userId) {
1674 final int callingUid = Binder.getCallingUid();
1675 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1676 userId, "stopRecording");
1677 final long identity = Binder.clearCallingIdentity();
1678 try {
1679 synchronized (mLock) {
1680 try {
1681 getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording();
1682 } catch (RemoteException | SessionNotFoundException e) {
1683 Slog.e(TAG, "error in stopRecording", e);
1684 }
1685 }
1686 } finally {
1687 Binder.restoreCallingIdentity(identity);
1688 }
1689 }
1690
1691 @Override
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001692 public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
Wonsik Kim969167d2014-06-24 16:33:17 +09001693 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001694 != PackageManager.PERMISSION_GRANTED) {
1695 return null;
1696 }
1697
1698 final long identity = Binder.clearCallingIdentity();
1699 try {
1700 return mTvInputHardwareManager.getHardwareList();
1701 } finally {
1702 Binder.restoreCallingIdentity(identity);
1703 }
1704 }
1705
1706 @Override
1707 public ITvInputHardware acquireTvInputHardware(int deviceId,
Wonsik Kim969167d2014-06-24 16:33:17 +09001708 ITvInputHardwareCallback callback, TvInputInfo info, int userId)
1709 throws RemoteException {
1710 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001711 != PackageManager.PERMISSION_GRANTED) {
1712 return null;
1713 }
1714
1715 final long identity = Binder.clearCallingIdentity();
1716 final int callingUid = Binder.getCallingUid();
1717 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1718 userId, "acquireTvInputHardware");
1719 try {
1720 return mTvInputHardwareManager.acquireHardware(
Wonsik Kim969167d2014-06-24 16:33:17 +09001721 deviceId, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001722 } finally {
1723 Binder.restoreCallingIdentity(identity);
1724 }
1725 }
1726
1727 @Override
1728 public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
1729 throws RemoteException {
Wonsik Kim969167d2014-06-24 16:33:17 +09001730 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001731 != PackageManager.PERMISSION_GRANTED) {
1732 return;
1733 }
1734
1735 final long identity = Binder.clearCallingIdentity();
1736 final int callingUid = Binder.getCallingUid();
1737 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1738 userId, "releaseTvInputHardware");
1739 try {
1740 mTvInputHardwareManager.releaseHardware(
1741 deviceId, hardware, callingUid, resolvedUserId);
1742 } finally {
1743 Binder.restoreCallingIdentity(identity);
1744 }
1745 }
Jaewan Kime14c3f42014-06-27 13:47:48 +09001746
1747 @Override
Jaesung Chung58739e72015-04-24 19:39:59 +09001748 public List<DvbDeviceInfo> getDvbDeviceList() throws RemoteException {
1749 if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
1750 != PackageManager.PERMISSION_GRANTED) {
1751 throw new SecurityException("Requires DVB_DEVICE permission");
1752 }
1753
1754 final long identity = Binder.clearCallingIdentity();
1755 try {
Jiabinef1659f2016-11-18 10:50:15 +09001756 // Pattern1: /dev/dvb%d.frontend%d
1757 ArrayList<DvbDeviceInfo> deviceInfosFromPattern1 = new ArrayList<>();
Jaesung Chung58739e72015-04-24 19:39:59 +09001758 File devDirectory = new File("/dev");
Jiabinef1659f2016-11-18 10:50:15 +09001759 boolean dvbDirectoryFound = false;
Jaesung Chung58739e72015-04-24 19:39:59 +09001760 for (String fileName : devDirectory.list()) {
1761 Matcher matcher = sFrontEndDevicePattern.matcher(fileName);
1762 if (matcher.find()) {
1763 int adapterId = Integer.parseInt(matcher.group(1));
1764 int deviceId = Integer.parseInt(matcher.group(2));
Jiabinef1659f2016-11-18 10:50:15 +09001765 deviceInfosFromPattern1.add(new DvbDeviceInfo(adapterId, deviceId));
1766 }
1767 if (TextUtils.equals("dvb", fileName)) {
1768 dvbDirectoryFound = true;
Jaesung Chung58739e72015-04-24 19:39:59 +09001769 }
1770 }
Jiabinef1659f2016-11-18 10:50:15 +09001771 if (!dvbDirectoryFound) {
1772 return Collections.unmodifiableList(deviceInfosFromPattern1);
1773 }
1774 File dvbDirectory = new File(DVB_DIRECTORY);
1775 // Pattern2: /dev/dvb/adapter%d/frontend%d
1776 ArrayList<DvbDeviceInfo> deviceInfosFromPattern2 = new ArrayList<>();
1777 for (String fileNameInDvb : dvbDirectory.list()) {
1778 Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
1779 if (adapterMatcher.find()) {
1780 int adapterId = Integer.parseInt(adapterMatcher.group(1));
1781 File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
1782 for (String fileNameInAdapter : adapterDirectory.list()) {
1783 Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
1784 fileNameInAdapter);
1785 if (frontendMatcher.find()) {
1786 int deviceId = Integer.parseInt(frontendMatcher.group(1));
1787 deviceInfosFromPattern2.add(
1788 new DvbDeviceInfo(adapterId, deviceId));
1789 }
1790 }
1791 }
1792 }
1793 return deviceInfosFromPattern2.isEmpty()
1794 ? Collections.unmodifiableList(deviceInfosFromPattern1)
1795 : Collections.unmodifiableList(deviceInfosFromPattern2);
Jaesung Chung58739e72015-04-24 19:39:59 +09001796 } finally {
1797 Binder.restoreCallingIdentity(identity);
1798 }
1799 }
1800
1801 @Override
1802 public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device)
1803 throws RemoteException {
1804 if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
1805 != PackageManager.PERMISSION_GRANTED) {
1806 throw new SecurityException("Requires DVB_DEVICE permission");
1807 }
1808
Jiabinef1659f2016-11-18 10:50:15 +09001809 File devDirectory = new File("/dev");
1810 boolean dvbDeviceFound = false;
1811 for (String fileName : devDirectory.list()) {
1812 if (TextUtils.equals("dvb", fileName)) {
1813 File dvbDirectory = new File(DVB_DIRECTORY);
1814 for (String fileNameInDvb : dvbDirectory.list()) {
1815 Matcher adapterMatcher = sAdapterDirPattern.matcher(fileNameInDvb);
1816 if (adapterMatcher.find()) {
1817 File adapterDirectory = new File(DVB_DIRECTORY + "/" + fileNameInDvb);
1818 for (String fileNameInAdapter : adapterDirectory.list()) {
1819 Matcher frontendMatcher = sFrontEndInAdapterDirPattern.matcher(
1820 fileNameInAdapter);
1821 if (frontendMatcher.find()) {
1822 dvbDeviceFound = true;
1823 break;
1824 }
1825 }
1826 }
1827 if (dvbDeviceFound) {
1828 break;
1829 }
1830 }
1831 }
1832 if (dvbDeviceFound) {
1833 break;
1834 }
1835 }
1836
Jaesung Chung58739e72015-04-24 19:39:59 +09001837 final long identity = Binder.clearCallingIdentity();
1838 try {
1839 String deviceFileName;
1840 switch (device) {
1841 case TvInputManager.DVB_DEVICE_DEMUX:
Jiabinef1659f2016-11-18 10:50:15 +09001842 deviceFileName = String.format(dvbDeviceFound
1843 ? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d",
1844 info.getAdapterId(), info.getDeviceId());
Jaesung Chung58739e72015-04-24 19:39:59 +09001845 break;
1846 case TvInputManager.DVB_DEVICE_DVR:
Jiabinef1659f2016-11-18 10:50:15 +09001847 deviceFileName = String.format(dvbDeviceFound
1848 ? "/dev/dvb/adapter%d/dvr%d" : "/dev/dvb%d.dvr%d",
1849 info.getAdapterId(), info.getDeviceId());
Jaesung Chung58739e72015-04-24 19:39:59 +09001850 break;
1851 case TvInputManager.DVB_DEVICE_FRONTEND:
Jiabinef1659f2016-11-18 10:50:15 +09001852 deviceFileName = String.format(dvbDeviceFound
1853 ? "/dev/dvb/adapter%d/frontend%d" : "/dev/dvb%d.frontend%d",
1854 info.getAdapterId(), info.getDeviceId());
Jaesung Chung58739e72015-04-24 19:39:59 +09001855 break;
1856 default:
1857 throw new IllegalArgumentException("Invalid DVB device: " + device);
1858 }
1859 try {
1860 // The DVB frontend device only needs to be opened in read/write mode, which
1861 // allows performing tuning operations. The DVB demux and DVR device are enough
1862 // to be opened in read only mode.
1863 return ParcelFileDescriptor.open(new File(deviceFileName),
1864 TvInputManager.DVB_DEVICE_FRONTEND == device
1865 ? ParcelFileDescriptor.MODE_READ_WRITE
1866 : ParcelFileDescriptor.MODE_READ_ONLY);
1867 } catch (FileNotFoundException e) {
1868 return null;
1869 }
1870 } finally {
1871 Binder.restoreCallingIdentity(identity);
1872 }
1873 }
1874
1875 @Override
Terry Heoc086a3d2014-06-18 14:26:44 +09001876 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
1877 throws RemoteException {
1878 if (mContext.checkCallingPermission(
1879 android.Manifest.permission.CAPTURE_TV_INPUT)
1880 != PackageManager.PERMISSION_GRANTED) {
1881 throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1882 }
1883
1884 final long identity = Binder.clearCallingIdentity();
1885 final int callingUid = Binder.getCallingUid();
1886 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1887 userId, "getAvailableTvStreamConfigList");
1888 try {
1889 return mTvInputHardwareManager.getAvailableTvStreamConfigList(
1890 inputId, callingUid, resolvedUserId);
1891 } finally {
1892 Binder.restoreCallingIdentity(identity);
1893 }
1894 }
1895
1896 @Override
1897 public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
1898 int userId)
1899 throws RemoteException {
1900 if (mContext.checkCallingPermission(
1901 android.Manifest.permission.CAPTURE_TV_INPUT)
1902 != PackageManager.PERMISSION_GRANTED) {
1903 throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1904 }
1905
1906 final long identity = Binder.clearCallingIdentity();
1907 final int callingUid = Binder.getCallingUid();
1908 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1909 userId, "captureFrame");
1910 try {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001911 String hardwareInputId = null;
Terry Heo79124a72014-07-21 15:17:17 +09001912 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001913 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001914 if (userState.inputMap.get(inputId) == null) {
Jae Seofea8dd42014-08-26 13:57:41 -07001915 Slog.e(TAG, "input not found for " + inputId);
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001916 return false;
1917 }
1918 for (SessionState sessionState : userState.sessionStateMap.values()) {
Jae Seo2cdb05e2016-02-04 22:17:13 +09001919 if (sessionState.inputId.equals(inputId)
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001920 && sessionState.hardwareSessionToken != null) {
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001921 hardwareInputId = userState.sessionStateMap.get(
Jae Seo2cdb05e2016-02-04 22:17:13 +09001922 sessionState.hardwareSessionToken).inputId;
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001923 break;
1924 }
1925 }
Terry Heo79124a72014-07-21 15:17:17 +09001926 }
Terry Heoc086a3d2014-06-18 14:26:44 +09001927 return mTvInputHardwareManager.captureFrame(
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09001928 (hardwareInputId != null) ? hardwareInputId : inputId,
Terry Heo79124a72014-07-21 15:17:17 +09001929 surface, config, callingUid, resolvedUserId);
Terry Heoc086a3d2014-06-18 14:26:44 +09001930 } finally {
1931 Binder.restoreCallingIdentity(identity);
1932 }
1933 }
1934
1935 @Override
Terry Heodf9f0a32014-08-06 13:53:33 +09001936 public boolean isSingleSessionActive(int userId) throws RemoteException {
1937 final long identity = Binder.clearCallingIdentity();
1938 final int callingUid = Binder.getCallingUid();
1939 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1940 userId, "isSingleSessionActive");
1941 try {
1942 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07001943 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
Terry Heodf9f0a32014-08-06 13:53:33 +09001944 if (userState.sessionStateMap.size() == 1) {
1945 return true;
Jae Seo93ff14b2015-06-21 14:08:54 -07001946 } else if (userState.sessionStateMap.size() == 2) {
Terry Heodf9f0a32014-08-06 13:53:33 +09001947 SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
Jae Seo93ff14b2015-06-21 14:08:54 -07001948 new SessionState[2]);
Terry Heodf9f0a32014-08-06 13:53:33 +09001949 // Check if there is a wrapper input.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09001950 if (sessionStates[0].hardwareSessionToken != null
1951 || sessionStates[1].hardwareSessionToken != null) {
Terry Heodf9f0a32014-08-06 13:53:33 +09001952 return true;
1953 }
1954 }
1955 return false;
1956 }
1957 } finally {
1958 Binder.restoreCallingIdentity(identity);
1959 }
1960 }
1961
1962 @Override
Dongwon Kang2e7f5ce2017-04-05 18:33:50 -07001963 public void requestChannelBrowsable(Uri channelUri, int userId)
1964 throws RemoteException {
1965 final String callingPackageName = getCallingPackageName();
1966 final long identity = Binder.clearCallingIdentity();
1967 final int callingUid = Binder.getCallingUid();
1968 final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1969 userId, "requestChannelBrowsable");
1970 try {
1971 Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
1972 List<ResolveInfo> list = getContext().getPackageManager()
1973 .queryBroadcastReceivers(intent, 0);
1974 if (list != null) {
1975 for (ResolveInfo info : list) {
1976 String receiverPackageName = info.activityInfo.packageName;
1977 intent.putExtra(TvContract.EXTRA_CHANNEL_ID, ContentUris.parseId(
1978 channelUri));
1979 intent.putExtra(TvContract.EXTRA_PACKAGE_NAME, callingPackageName);
1980 intent.setPackage(receiverPackageName);
1981 getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
1982 }
1983 }
1984 } finally {
1985 Binder.restoreCallingIdentity(identity);
1986 }
1987 }
1988
1989 @Override
Jae Seo0f8fc342014-07-02 10:47:08 -07001990 @SuppressWarnings("resource")
Jaewan Kime14c3f42014-06-27 13:47:48 +09001991 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1992 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001993 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Jaewan Kime14c3f42014-06-27 13:47:48 +09001994
1995 synchronized (mLock) {
1996 pw.println("User Ids (Current user: " + mCurrentUserId + "):");
1997 pw.increaseIndent();
1998 for (int i = 0; i < mUserStates.size(); i++) {
1999 int userId = mUserStates.keyAt(i);
2000 pw.println(Integer.valueOf(userId));
2001 }
2002 pw.decreaseIndent();
2003
2004 for (int i = 0; i < mUserStates.size(); i++) {
2005 int userId = mUserStates.keyAt(i);
Jae Seo4f1a6d42015-07-20 16:15:01 -07002006 UserState userState = getOrCreateUserStateLocked(userId);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002007 pw.println("UserState (" + userId + "):");
2008 pw.increaseIndent();
2009
Wonsik Kim969167d2014-06-24 16:33:17 +09002010 pw.println("inputMap: inputId -> TvInputState");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002011 pw.increaseIndent();
Jaewan Kim8e6b51b2014-07-15 13:01:57 +09002012 for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) {
2013 pw.println(entry.getKey() + ": " + entry.getValue());
Jaewan Kime14c3f42014-06-27 13:47:48 +09002014 }
2015 pw.decreaseIndent();
2016
Wonsik Kim969167d2014-06-24 16:33:17 +09002017 pw.println("packageSet:");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002018 pw.increaseIndent();
Wonsik Kim969167d2014-06-24 16:33:17 +09002019 for (String packageName : userState.packageSet) {
Jaewan Kime14c3f42014-06-27 13:47:48 +09002020 pw.println(packageName);
2021 }
2022 pw.decreaseIndent();
2023
2024 pw.println("clientStateMap: ITvInputClient -> ClientState");
2025 pw.increaseIndent();
2026 for (Map.Entry<IBinder, ClientState> entry :
2027 userState.clientStateMap.entrySet()) {
2028 ClientState client = entry.getValue();
2029 pw.println(entry.getKey() + ": " + client);
2030
2031 pw.increaseIndent();
2032
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002033 pw.println("sessionTokens:");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002034 pw.increaseIndent();
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002035 for (IBinder token : client.sessionTokens) {
Jaewan Kime14c3f42014-06-27 13:47:48 +09002036 pw.println("" + token);
2037 }
2038 pw.decreaseIndent();
2039
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002040 pw.println("clientTokens: " + client.clientToken);
2041 pw.println("userId: " + client.userId);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002042
2043 pw.decreaseIndent();
2044 }
2045 pw.decreaseIndent();
2046
Wonsik Kim187423c2014-06-25 14:12:48 +09002047 pw.println("serviceStateMap: ComponentName -> ServiceState");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002048 pw.increaseIndent();
Wonsik Kim187423c2014-06-25 14:12:48 +09002049 for (Map.Entry<ComponentName, ServiceState> entry :
Jaewan Kime14c3f42014-06-27 13:47:48 +09002050 userState.serviceStateMap.entrySet()) {
2051 ServiceState service = entry.getValue();
2052 pw.println(entry.getKey() + ": " + service);
2053
2054 pw.increaseIndent();
2055
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002056 pw.println("sessionTokens:");
Jaewan Kime14c3f42014-06-27 13:47:48 +09002057 pw.increaseIndent();
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002058 for (IBinder token : service.sessionTokens) {
Jaewan Kime14c3f42014-06-27 13:47:48 +09002059 pw.println("" + token);
2060 }
2061 pw.decreaseIndent();
2062
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002063 pw.println("service: " + service.service);
2064 pw.println("callback: " + service.callback);
2065 pw.println("bound: " + service.bound);
2066 pw.println("reconnecting: " + service.reconnecting);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002067
2068 pw.decreaseIndent();
2069 }
2070 pw.decreaseIndent();
2071
2072 pw.println("sessionStateMap: ITvInputSession -> SessionState");
2073 pw.increaseIndent();
2074 for (Map.Entry<IBinder, SessionState> entry :
2075 userState.sessionStateMap.entrySet()) {
2076 SessionState session = entry.getValue();
2077 pw.println(entry.getKey() + ": " + session);
2078
2079 pw.increaseIndent();
Jae Seo2cdb05e2016-02-04 22:17:13 +09002080 pw.println("inputId: " + session.inputId);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002081 pw.println("client: " + session.client);
2082 pw.println("seq: " + session.seq);
2083 pw.println("callingUid: " + session.callingUid);
2084 pw.println("userId: " + session.userId);
2085 pw.println("sessionToken: " + session.sessionToken);
2086 pw.println("session: " + session.session);
2087 pw.println("logUri: " + session.logUri);
2088 pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002089 pw.decreaseIndent();
2090 }
2091 pw.decreaseIndent();
2092
Wonsik Kim969167d2014-06-24 16:33:17 +09002093 pw.println("callbackSet:");
2094 pw.increaseIndent();
2095 for (ITvInputManagerCallback callback : userState.callbackSet) {
2096 pw.println(callback.toString());
2097 }
2098 pw.decreaseIndent();
2099
Ji-Hwan Lee956afc22014-07-26 11:31:39 +09002100 pw.println("mainSessionToken: " + userState.mainSessionToken);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002101 pw.decreaseIndent();
2102 }
2103 }
yangren8d718e12016-02-15 17:02:49 +09002104 mTvInputHardwareManager.dump(fd, writer, args);
Jaewan Kime14c3f42014-06-27 13:47:48 +09002105 }
Jae Seo39570912014-02-20 18:23:25 -08002106 }
2107
Wonsik Kim969167d2014-06-24 16:33:17 +09002108 private static final class UserState {
2109 // A mapping from the TV input id to its TvInputState.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002110 private Map<String, TvInputState> inputMap = new HashMap<>();
Wonsik Kim969167d2014-06-24 16:33:17 +09002111
2112 // A set of all TV input packages.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002113 private final Set<String> packageSet = new HashSet<>();
Jae Seo5c80ad22014-06-12 19:52:58 -07002114
Jae Seo9c165d62014-08-25 14:39:26 -07002115 // A list of all TV content rating systems defined.
2116 private final List<TvContentRatingSystemInfo>
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002117 contentRatingSystemList = new ArrayList<>();
Sungsoo Lim5c5b83f2014-07-29 11:48:36 +09002118
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002119 // A mapping from the token of a client to its state.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002120 private final Map<IBinder, ClientState> clientStateMap = new HashMap<>();
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002121
Jae Seo39570912014-02-20 18:23:25 -08002122 // A mapping from the name of a TV input service to its state.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002123 private final Map<ComponentName, ServiceState> serviceStateMap = new HashMap<>();
Jae Seo39570912014-02-20 18:23:25 -08002124
2125 // A mapping from the token of a TV input session to its state.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002126 private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>();
Wonsik Kim969167d2014-06-24 16:33:17 +09002127
2128 // A set of callbacks.
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002129 private final Set<ITvInputManagerCallback> callbackSet = new HashSet<>();
Terry Heo79124a72014-07-21 15:17:17 +09002130
Ji-Hwan Lee4c526972014-07-22 04:46:30 +09002131 // The token of a "main" TV input session.
2132 private IBinder mainSessionToken = null;
Jae Seo783645e2014-07-28 17:30:50 +09002133
2134 // Persistent data store for all internal settings maintained by the TV input manager
2135 // service.
2136 private final PersistentDataStore persistentDataStore;
2137
2138 private UserState(Context context, int userId) {
2139 persistentDataStore = new PersistentDataStore(context, userId);
2140 }
Jae Seo39570912014-02-20 18:23:25 -08002141 }
2142
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002143 private final class ClientState implements IBinder.DeathRecipient {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002144 private final List<IBinder> sessionTokens = new ArrayList<>();
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002145
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002146 private IBinder clientToken;
2147 private final int userId;
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002148
2149 ClientState(IBinder clientToken, int userId) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002150 this.clientToken = clientToken;
2151 this.userId = userId;
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002152 }
2153
2154 public boolean isEmpty() {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002155 return sessionTokens.isEmpty();
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002156 }
2157
2158 @Override
2159 public void binderDied() {
2160 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002161 UserState userState = getOrCreateUserStateLocked(userId);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002162 // DO NOT remove the client state of clientStateMap in this method. It will be
Ji-Hwan Leea65118e2014-07-24 16:30:02 +09002163 // removed in releaseSessionLocked().
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002164 ClientState clientState = userState.clientStateMap.get(clientToken);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002165 if (clientState != null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002166 while (clientState.sessionTokens.size() > 0) {
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002167 releaseSessionLocked(
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002168 clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002169 }
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002170 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002171 clientToken = null;
Sungsoo Lim72ad7bf2014-05-14 09:21:08 +09002172 }
2173 }
2174 }
2175
Jae Seo39570912014-02-20 18:23:25 -08002176 private final class ServiceState {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002177 private final List<IBinder> sessionTokens = new ArrayList<>();
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002178 private final ServiceConnection connection;
2179 private final ComponentName component;
2180 private final boolean isHardware;
Shubang71d5c762017-02-23 15:46:40 -08002181 private final Map<String, TvInputInfo> hardwareInputMap = new HashMap<>();
Jae Seo39570912014-02-20 18:23:25 -08002182
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002183 private ITvInputService service;
2184 private ServiceCallback callback;
2185 private boolean bound;
2186 private boolean reconnecting;
Jae Seo39570912014-02-20 18:23:25 -08002187
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002188 private ServiceState(ComponentName component, int userId) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002189 this.component = component;
2190 this.connection = new InputServiceConnection(component, userId);
2191 this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
2192 }
2193 }
2194
2195 private static final class TvInputState {
2196 // A TvInputInfo object which represents the TV input.
2197 private TvInputInfo info;
2198
2199 // The state of TV input. Connected by default.
2200 private int state = INPUT_STATE_CONNECTED;
2201
2202 @Override
2203 public String toString() {
2204 return "info: " + info + "; state: " + state;
Jae Seo39570912014-02-20 18:23:25 -08002205 }
2206 }
2207
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002208 private final class SessionState implements IBinder.DeathRecipient {
Jae Seo2cdb05e2016-02-04 22:17:13 +09002209 private final String inputId;
2210 private final ComponentName componentName;
Jae Seoa826d012016-01-18 13:03:35 -08002211 private final boolean isRecordingSession;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002212 private final ITvInputClient client;
2213 private final int seq;
2214 private final int callingUid;
2215 private final int userId;
2216 private final IBinder sessionToken;
2217 private ITvInputSession session;
2218 private Uri logUri;
Dongwon Kangbd2fa2c2014-07-25 19:52:08 +09002219 // Not null if this session represents an external device connected to a hardware TV input.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002220 private IBinder hardwareSessionToken;
Jae Seo39570912014-02-20 18:23:25 -08002221
Jae Seo2cdb05e2016-02-04 22:17:13 +09002222 private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
2223 boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
2224 int userId) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002225 this.sessionToken = sessionToken;
Jae Seo2cdb05e2016-02-04 22:17:13 +09002226 this.inputId = inputId;
2227 this.componentName = componentName;
Jae Seoa826d012016-01-18 13:03:35 -08002228 this.isRecordingSession = isRecordingSession;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002229 this.client = client;
2230 this.seq = seq;
2231 this.callingUid = callingUid;
2232 this.userId = userId;
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002233 }
2234
2235 @Override
2236 public void binderDied() {
2237 synchronized (mLock) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002238 session = null;
shubang8049f202016-04-25 11:21:42 -07002239 clearSessionAndNotifyClientLocked(this);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002240 }
Jae Seo39570912014-02-20 18:23:25 -08002241 }
2242 }
2243
2244 private final class InputServiceConnection implements ServiceConnection {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002245 private final ComponentName mComponent;
Jae Seo39570912014-02-20 18:23:25 -08002246 private final int mUserId;
2247
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002248 private InputServiceConnection(ComponentName component, int userId) {
2249 mComponent = component;
Jae Seo39570912014-02-20 18:23:25 -08002250 mUserId = userId;
2251 }
2252
2253 @Override
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002254 public void onServiceConnected(ComponentName component, IBinder service) {
Jae Seo39570912014-02-20 18:23:25 -08002255 if (DEBUG) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002256 Slog.d(TAG, "onServiceConnected(component=" + component + ")");
Jae Seo39570912014-02-20 18:23:25 -08002257 }
2258 synchronized (mLock) {
Dongwon Kang81e3c3e2015-09-11 15:24:25 -07002259 UserState userState = mUserStates.get(mUserId);
2260 if (userState == null) {
2261 // The user was removed while connecting.
2262 mContext.unbindService(this);
2263 return;
2264 }
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002265 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002266 serviceState.service = ITvInputService.Stub.asInterface(service);
Jae Seo39570912014-02-20 18:23:25 -08002267
2268 // Register a callback, if we need to.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002269 if (serviceState.isHardware && serviceState.callback == null) {
2270 serviceState.callback = new ServiceCallback(mComponent, mUserId);
Jae Seo39570912014-02-20 18:23:25 -08002271 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002272 serviceState.service.registerCallback(serviceState.callback);
Jae Seo39570912014-02-20 18:23:25 -08002273 } catch (RemoteException e) {
Youngsang Cho9a22f0f2014-04-09 22:51:54 +09002274 Slog.e(TAG, "error in registerCallback", e);
Jae Seo39570912014-02-20 18:23:25 -08002275 }
2276 }
2277
2278 // And create sessions, if any.
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002279 for (IBinder sessionToken : serviceState.sessionTokens) {
2280 createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
Jae Seo39570912014-02-20 18:23:25 -08002281 }
Wonsik Kim969167d2014-06-24 16:33:17 +09002282
Wonsik Kim187423c2014-06-25 14:12:48 +09002283 for (TvInputState inputState : userState.inputMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002284 if (inputState.info.getComponent().equals(component)
Jae Seo82fce642015-04-20 15:37:50 -07002285 && inputState.state != INPUT_STATE_CONNECTED) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002286 notifyInputStateChangedLocked(userState, inputState.info.getId(),
2287 inputState.state, null);
Wonsik Kim187423c2014-06-25 14:12:48 +09002288 }
2289 }
2290
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002291 if (serviceState.isHardware) {
Shubang71d5c762017-02-23 15:46:40 -08002292 serviceState.hardwareInputMap.clear();
Jae Seoc980f43d2016-02-09 23:46:58 -08002293 for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) {
Wonsik Kim187423c2014-06-25 14:12:48 +09002294 try {
Jae Seoc980f43d2016-02-09 23:46:58 -08002295 serviceState.service.notifyHardwareAdded(hardware);
Wonsik Kim187423c2014-06-25 14:12:48 +09002296 } catch (RemoteException e) {
2297 Slog.e(TAG, "error in notifyHardwareAdded", e);
2298 }
2299 }
Jae Seoc980f43d2016-02-09 23:46:58 -08002300 for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002301 try {
Jae Seoc980f43d2016-02-09 23:46:58 -08002302 serviceState.service.notifyHdmiDeviceAdded(device);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002303 } catch (RemoteException e) {
Jae Seo546c6352014-08-07 11:57:01 -07002304 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002305 }
2306 }
Wonsik Kim969167d2014-06-24 16:33:17 +09002307 }
Jae Seo39570912014-02-20 18:23:25 -08002308 }
2309 }
2310
2311 @Override
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002312 public void onServiceDisconnected(ComponentName component) {
Jae Seo39570912014-02-20 18:23:25 -08002313 if (DEBUG) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002314 Slog.d(TAG, "onServiceDisconnected(component=" + component + ")");
Jae Seo39570912014-02-20 18:23:25 -08002315 }
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002316 if (!mComponent.equals(component)) {
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002317 throw new IllegalArgumentException("Mismatched ComponentName: "
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002318 + mComponent + " (expected), " + component + " (actual).");
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002319 }
2320 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002321 UserState userState = getOrCreateUserStateLocked(mUserId);
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002322 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002323 if (serviceState != null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002324 serviceState.reconnecting = true;
2325 serviceState.bound = false;
2326 serviceState.service = null;
2327 serviceState.callback = null;
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002328
Dongwon Kang426c9a42014-08-26 17:39:21 +09002329 abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
Sungsoo Lim2b35a722014-04-17 17:09:15 +09002330 }
2331 }
Jae Seo39570912014-02-20 18:23:25 -08002332 }
2333 }
2334
2335 private final class ServiceCallback extends ITvInputServiceCallback.Stub {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002336 private final ComponentName mComponent;
Jae Seo39570912014-02-20 18:23:25 -08002337 private final int mUserId;
2338
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002339 ServiceCallback(ComponentName component, int userId) {
2340 mComponent = component;
Jae Seo39570912014-02-20 18:23:25 -08002341 mUserId = userId;
2342 }
2343
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002344 private void ensureHardwarePermission() {
2345 if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2346 != PackageManager.PERMISSION_GRANTED) {
2347 throw new SecurityException("The caller does not have hardware permission");
2348 }
2349 }
Wonsik Kim187423c2014-06-25 14:12:48 +09002350
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002351 private void ensureValidInput(TvInputInfo inputInfo) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002352 if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002353 throw new IllegalArgumentException("Invalid TvInputInfo");
2354 }
2355 }
2356
Jae Seo1abbbcd2016-01-28 22:20:41 -08002357 private void addHardwareInputLocked(TvInputInfo inputInfo) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002358 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
Shubang71d5c762017-02-23 15:46:40 -08002359 serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
Chulwoo Lee19ba61a2014-09-03 00:59:35 +09002360 buildTvInputListLocked(mUserId, null);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002361 }
2362
Jae Seo1abbbcd2016-01-28 22:20:41 -08002363 public void addHardwareInput(int deviceId, TvInputInfo inputInfo) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002364 ensureHardwarePermission();
2365 ensureValidInput(inputInfo);
2366 synchronized (mLock) {
Jae Seo1abbbcd2016-01-28 22:20:41 -08002367 mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
2368 addHardwareInputLocked(inputInfo);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002369 }
2370 }
2371
Jae Seo1abbbcd2016-01-28 22:20:41 -08002372 public void addHdmiInput(int id, TvInputInfo inputInfo) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002373 ensureHardwarePermission();
2374 ensureValidInput(inputInfo);
2375 synchronized (mLock) {
Jae Seo1abbbcd2016-01-28 22:20:41 -08002376 mTvInputHardwareManager.addHdmiInput(id, inputInfo);
2377 addHardwareInputLocked(inputInfo);
Wonsik Kim187423c2014-06-25 14:12:48 +09002378 }
2379 }
2380
Jae Seo1abbbcd2016-01-28 22:20:41 -08002381 public void removeHardwareInput(String inputId) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002382 ensureHardwarePermission();
Wonsik Kim187423c2014-06-25 14:12:48 +09002383 synchronized (mLock) {
Ji-Hwan Lee9e8ade22014-07-25 20:20:38 +09002384 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
Shubang71d5c762017-02-23 15:46:40 -08002385 boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
Wonsik Kim187423c2014-06-25 14:12:48 +09002386 if (removed) {
Chulwoo Lee19ba61a2014-09-03 00:59:35 +09002387 buildTvInputListLocked(mUserId, null);
Jae Seo1abbbcd2016-01-28 22:20:41 -08002388 mTvInputHardwareManager.removeHardwareInput(inputId);
Wonsik Kim187423c2014-06-25 14:12:48 +09002389 } else {
Jae Seofea8dd42014-08-26 13:57:41 -07002390 Slog.e(TAG, "failed to remove input " + inputId);
Wonsik Kim187423c2014-06-25 14:12:48 +09002391 }
Jae Seo39570912014-02-20 18:23:25 -08002392 }
2393 }
2394 }
Jae Seo31dc634be2014-04-15 17:40:23 -07002395
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002396 private final class SessionCallback extends ITvInputSessionCallback.Stub {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002397 private final SessionState mSessionState;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002398 private final InputChannel[] mChannels;
2399
2400 SessionCallback(SessionState sessionState, InputChannel[] channels) {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002401 mSessionState = sessionState;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002402 mChannels = channels;
2403 }
2404
2405 @Override
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002406 public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002407 if (DEBUG) {
Jae Seo2cdb05e2016-02-04 22:17:13 +09002408 Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")");
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002409 }
2410 synchronized (mLock) {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002411 mSessionState.session = session;
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002412 mSessionState.hardwareSessionToken = hardwareSessionToken;
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002413 if (session != null && addSessionTokenToClientStateLocked(session)) {
2414 sendSessionTokenToClientLocked(mSessionState.client,
Jae Seo2cdb05e2016-02-04 22:17:13 +09002415 mSessionState.inputId, mSessionState.sessionToken, mChannels[0],
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002416 mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002417 } else {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002418 removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId);
2419 sendSessionTokenToClientLocked(mSessionState.client,
Jae Seo2cdb05e2016-02-04 22:17:13 +09002420 mSessionState.inputId, null, null, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002421 }
2422 mChannels[0].dispose();
2423 }
2424 }
2425
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002426 private boolean addSessionTokenToClientStateLocked(ITvInputSession session) {
2427 try {
2428 session.asBinder().linkToDeath(mSessionState, 0);
2429 } catch (RemoteException e) {
2430 Slog.e(TAG, "session process has already died", e);
2431 return false;
2432 }
2433
2434 IBinder clientToken = mSessionState.client.asBinder();
Jae Seo4f1a6d42015-07-20 16:15:01 -07002435 UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002436 ClientState clientState = userState.clientStateMap.get(clientToken);
2437 if (clientState == null) {
2438 clientState = new ClientState(clientToken, mSessionState.userId);
2439 try {
2440 clientToken.linkToDeath(clientState, 0);
2441 } catch (RemoteException e) {
2442 Slog.e(TAG, "client process has already died", e);
2443 return false;
2444 }
2445 userState.clientStateMap.put(clientToken, clientState);
2446 }
2447 clientState.sessionTokens.add(mSessionState.sessionToken);
2448 return true;
2449 }
2450
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002451 @Override
2452 public void onChannelRetuned(Uri channelUri) {
2453 synchronized (mLock) {
2454 if (DEBUG) {
2455 Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
2456 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002457 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002458 return;
2459 }
2460 try {
2461 // TODO: Consider adding this channel change in the watch log. When we do
2462 // that, how we can protect the watch log from malicious tv inputs should
2463 // be addressed. e.g. add a field which represents where the channel change
2464 // originated from.
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002465 mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002466 } catch (RemoteException e) {
2467 Slog.e(TAG, "error in onChannelRetuned", e);
2468 }
2469 }
2470 }
2471
2472 @Override
2473 public void onTracksChanged(List<TvTrackInfo> tracks) {
2474 synchronized (mLock) {
2475 if (DEBUG) {
2476 Slog.d(TAG, "onTracksChanged(" + tracks + ")");
2477 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002478 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002479 return;
2480 }
2481 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002482 mSessionState.client.onTracksChanged(tracks, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002483 } catch (RemoteException e) {
2484 Slog.e(TAG, "error in onTracksChanged", e);
2485 }
2486 }
2487 }
2488
2489 @Override
2490 public void onTrackSelected(int type, String trackId) {
2491 synchronized (mLock) {
2492 if (DEBUG) {
2493 Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
2494 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002495 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002496 return;
2497 }
2498 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002499 mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002500 } catch (RemoteException e) {
2501 Slog.e(TAG, "error in onTrackSelected", e);
2502 }
2503 }
2504 }
2505
2506 @Override
2507 public void onVideoAvailable() {
2508 synchronized (mLock) {
2509 if (DEBUG) {
2510 Slog.d(TAG, "onVideoAvailable()");
2511 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002512 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002513 return;
2514 }
2515 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002516 mSessionState.client.onVideoAvailable(mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002517 } catch (RemoteException e) {
2518 Slog.e(TAG, "error in onVideoAvailable", e);
2519 }
2520 }
2521 }
2522
2523 @Override
2524 public void onVideoUnavailable(int reason) {
2525 synchronized (mLock) {
2526 if (DEBUG) {
2527 Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
2528 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002529 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002530 return;
2531 }
2532 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002533 mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002534 } catch (RemoteException e) {
2535 Slog.e(TAG, "error in onVideoUnavailable", e);
2536 }
2537 }
2538 }
2539
2540 @Override
2541 public void onContentAllowed() {
2542 synchronized (mLock) {
2543 if (DEBUG) {
2544 Slog.d(TAG, "onContentAllowed()");
2545 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002546 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002547 return;
2548 }
2549 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002550 mSessionState.client.onContentAllowed(mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002551 } catch (RemoteException e) {
2552 Slog.e(TAG, "error in onContentAllowed", e);
2553 }
2554 }
2555 }
2556
2557 @Override
2558 public void onContentBlocked(String rating) {
2559 synchronized (mLock) {
2560 if (DEBUG) {
2561 Slog.d(TAG, "onContentBlocked()");
2562 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002563 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002564 return;
2565 }
2566 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002567 mSessionState.client.onContentBlocked(rating, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002568 } catch (RemoteException e) {
2569 Slog.e(TAG, "error in onContentBlocked", e);
2570 }
2571 }
2572 }
2573
2574 @Override
2575 public void onLayoutSurface(int left, int top, int right, int bottom) {
2576 synchronized (mLock) {
2577 if (DEBUG) {
2578 Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
2579 + ", right=" + right + ", bottom=" + bottom + ",)");
2580 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002581 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002582 return;
2583 }
2584 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002585 mSessionState.client.onLayoutSurface(left, top, right, bottom,
2586 mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002587 } catch (RemoteException e) {
2588 Slog.e(TAG, "error in onLayoutSurface", e);
2589 }
2590 }
2591 }
2592
2593 @Override
2594 public void onSessionEvent(String eventType, Bundle eventArgs) {
2595 synchronized (mLock) {
2596 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002597 Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs
2598 + ")");
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002599 }
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002600 if (mSessionState.session == null || mSessionState.client == null) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002601 return;
2602 }
2603 try {
Ji-Hwan Lee9c6b5b72014-09-17 00:22:39 +09002604 mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002605 } catch (RemoteException e) {
2606 Slog.e(TAG, "error in onSessionEvent", e);
2607 }
2608 }
2609 }
Dongwon Kang6f0240c2015-03-31 17:56:36 -07002610
2611 @Override
2612 public void onTimeShiftStatusChanged(int status) {
2613 synchronized (mLock) {
2614 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002615 Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07002616 }
2617 if (mSessionState.session == null || mSessionState.client == null) {
2618 return;
2619 }
2620 try {
2621 mSessionState.client.onTimeShiftStatusChanged(status, mSessionState.seq);
2622 } catch (RemoteException e) {
2623 Slog.e(TAG, "error in onTimeShiftStatusChanged", e);
2624 }
2625 }
2626 }
2627
2628 @Override
2629 public void onTimeShiftStartPositionChanged(long timeMs) {
2630 synchronized (mLock) {
2631 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002632 Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07002633 }
2634 if (mSessionState.session == null || mSessionState.client == null) {
2635 return;
2636 }
2637 try {
2638 mSessionState.client.onTimeShiftStartPositionChanged(timeMs, mSessionState.seq);
2639 } catch (RemoteException e) {
2640 Slog.e(TAG, "error in onTimeShiftStartPositionChanged", e);
2641 }
2642 }
2643 }
2644
2645 @Override
2646 public void onTimeShiftCurrentPositionChanged(long timeMs) {
2647 synchronized (mLock) {
2648 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002649 Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")");
Dongwon Kang6f0240c2015-03-31 17:56:36 -07002650 }
2651 if (mSessionState.session == null || mSessionState.client == null) {
2652 return;
2653 }
2654 try {
2655 mSessionState.client.onTimeShiftCurrentPositionChanged(timeMs,
2656 mSessionState.seq);
2657 } catch (RemoteException e) {
2658 Slog.e(TAG, "error in onTimeShiftCurrentPositionChanged", e);
2659 }
2660 }
2661 }
Jae Seoa826d012016-01-18 13:03:35 -08002662
2663 // For the recording session only
2664 @Override
Dongwon Kangb55c7512016-03-01 09:36:07 -08002665 public void onTuned(Uri channelUri) {
Jae Seoa826d012016-01-18 13:03:35 -08002666 synchronized (mLock) {
2667 if (DEBUG) {
Jae Seoe3c11e82016-02-08 23:18:49 -08002668 Slog.d(TAG, "onTuned()");
Jae Seoa826d012016-01-18 13:03:35 -08002669 }
2670 if (mSessionState.session == null || mSessionState.client == null) {
2671 return;
2672 }
2673 try {
Dongwon Kangb55c7512016-03-01 09:36:07 -08002674 mSessionState.client.onTuned(mSessionState.seq, channelUri);
Jae Seoa826d012016-01-18 13:03:35 -08002675 } catch (RemoteException e) {
Jae Seoe3c11e82016-02-08 23:18:49 -08002676 Slog.e(TAG, "error in onTuned", e);
Jae Seoa826d012016-01-18 13:03:35 -08002677 }
2678 }
2679 }
2680
2681 // For the recording session only
2682 @Override
2683 public void onRecordingStopped(Uri recordedProgramUri) {
2684 synchronized (mLock) {
2685 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002686 Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri
2687 + ")");
Jae Seoa826d012016-01-18 13:03:35 -08002688 }
2689 if (mSessionState.session == null || mSessionState.client == null) {
2690 return;
2691 }
2692 try {
2693 mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq);
2694 } catch (RemoteException e) {
2695 Slog.e(TAG, "error in onRecordingStopped", e);
2696 }
2697 }
2698 }
2699
2700 // For the recording session only
2701 @Override
2702 public void onError(int error) {
2703 synchronized (mLock) {
2704 if (DEBUG) {
Jae Seo4eee6a72016-02-06 11:11:35 +09002705 Slog.d(TAG, "onError(error=" + error + ")");
Jae Seoa826d012016-01-18 13:03:35 -08002706 }
2707 if (mSessionState.session == null || mSessionState.client == null) {
2708 return;
2709 }
2710 try {
2711 mSessionState.client.onError(error, mSessionState.seq);
2712 } catch (RemoteException e) {
2713 Slog.e(TAG, "error in onError", e);
2714 }
2715 }
2716 }
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002717 }
2718
2719 private static final class WatchLogHandler extends Handler {
Jae Seo7eb75df2014-08-08 22:20:48 -07002720 // There are only two kinds of watch events that can happen on the system:
2721 // 1. The current TV input session is tuned to a new channel.
2722 // 2. The session is released for some reason.
2723 // The former indicates the end of the previous log entry, if any, followed by the start of
2724 // a new entry. The latter indicates the end of the most recent entry for the given session.
2725 // Here the system supplies the database the smallest set of information only that is
2726 // sufficient to consolidate the log entries while minimizing database operations in the
2727 // system service.
Jae Seo8c375fe2015-06-23 20:33:25 -07002728 static final int MSG_LOG_WATCH_START = 1;
2729 static final int MSG_LOG_WATCH_END = 2;
2730 static final int MSG_SWITCH_CONTENT_RESOLVER = 3;
Jae Seo31dc634be2014-04-15 17:40:23 -07002731
Jae Seo8c375fe2015-06-23 20:33:25 -07002732 private ContentResolver mContentResolver;
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002733
Jae Seo8c375fe2015-06-23 20:33:25 -07002734 WatchLogHandler(ContentResolver contentResolver, Looper looper) {
Jae Seo31dc634be2014-04-15 17:40:23 -07002735 super(looper);
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002736 mContentResolver = contentResolver;
Jae Seo31dc634be2014-04-15 17:40:23 -07002737 }
2738
2739 @Override
2740 public void handleMessage(Message msg) {
2741 switch (msg.what) {
Jae Seo7eb75df2014-08-08 22:20:48 -07002742 case MSG_LOG_WATCH_START: {
Jae Seo31dc634be2014-04-15 17:40:23 -07002743 SomeArgs args = (SomeArgs) msg.obj;
Jae Seo7eb75df2014-08-08 22:20:48 -07002744 String packageName = (String) args.arg1;
2745 long watchStartTime = (long) args.arg2;
2746 long channelId = (long) args.arg3;
2747 Bundle tuneParams = (Bundle) args.arg4;
2748 IBinder sessionToken = (IBinder) args.arg5;
2749
2750 ContentValues values = new ContentValues();
2751 values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
2752 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
2753 watchStartTime);
2754 values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
2755 if (tuneParams != null) {
2756 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS,
2757 encodeTuneParams(tuneParams));
2758 }
2759 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
2760 sessionToken.toString());
2761
2762 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
Jae Seo31dc634be2014-04-15 17:40:23 -07002763 args.recycle();
Jae Seo8c375fe2015-06-23 20:33:25 -07002764 break;
Jae Seo31dc634be2014-04-15 17:40:23 -07002765 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002766 case MSG_LOG_WATCH_END: {
Jae Seo31dc634be2014-04-15 17:40:23 -07002767 SomeArgs args = (SomeArgs) msg.obj;
Jae Seo7eb75df2014-08-08 22:20:48 -07002768 IBinder sessionToken = (IBinder) args.arg1;
2769 long watchEndTime = (long) args.arg2;
2770
2771 ContentValues values = new ContentValues();
2772 values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
2773 watchEndTime);
2774 values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
2775 sessionToken.toString());
2776
2777 mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
Jae Seo31dc634be2014-04-15 17:40:23 -07002778 args.recycle();
Jae Seo8c375fe2015-06-23 20:33:25 -07002779 break;
2780 }
2781 case MSG_SWITCH_CONTENT_RESOLVER: {
2782 mContentResolver = (ContentResolver) msg.obj;
2783 break;
Jae Seo31dc634be2014-04-15 17:40:23 -07002784 }
2785 default: {
Jae Seo8c375fe2015-06-23 20:33:25 -07002786 Slog.w(TAG, "unhandled message code: " + msg.what);
2787 break;
Jae Seo31dc634be2014-04-15 17:40:23 -07002788 }
2789 }
2790 }
2791
Jae Seo7eb75df2014-08-08 22:20:48 -07002792 private String encodeTuneParams(Bundle tuneParams) {
2793 StringBuilder builder = new StringBuilder();
2794 Set<String> keySet = tuneParams.keySet();
2795 Iterator<String> it = keySet.iterator();
2796 while (it.hasNext()) {
2797 String key = it.next();
2798 Object value = tuneParams.get(key);
2799 if (value == null) {
2800 continue;
Jae Seo579befe2014-08-06 19:18:37 -07002801 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002802 builder.append(replaceEscapeCharacters(key));
2803 builder.append("=");
2804 builder.append(replaceEscapeCharacters(value.toString()));
2805 if (it.hasNext()) {
2806 builder.append(", ");
Jae Seo31dc634be2014-04-15 17:40:23 -07002807 }
2808 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002809 return builder.toString();
Jae Seo31dc634be2014-04-15 17:40:23 -07002810 }
2811
Jae Seo7eb75df2014-08-08 22:20:48 -07002812 private String replaceEscapeCharacters(String src) {
2813 final char ESCAPE_CHARACTER = '%';
2814 final String ENCODING_TARGET_CHARACTERS = "%=,";
2815 StringBuilder builder = new StringBuilder();
2816 for (char ch : src.toCharArray()) {
2817 if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) {
2818 builder.append(ESCAPE_CHARACTER);
Chulwoo Lee8d4ded02014-07-10 03:56:39 +09002819 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002820 builder.append(ch);
Jae Seo31dc634be2014-04-15 17:40:23 -07002821 }
Jae Seo7eb75df2014-08-08 22:20:48 -07002822 return builder.toString();
Jae Seo579befe2014-08-06 19:18:37 -07002823 }
Jae Seo31dc634be2014-04-15 17:40:23 -07002824 }
Wonsik Kim969167d2014-06-24 16:33:17 +09002825
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002826 private final class HardwareListener implements TvInputHardwareManager.Listener {
Wonsik Kim187423c2014-06-25 14:12:48 +09002827 @Override
2828 public void onStateChanged(String inputId, int state) {
Wonsik Kim969167d2014-06-24 16:33:17 +09002829 synchronized (mLock) {
2830 setStateLocked(inputId, state, mCurrentUserId);
2831 }
2832 }
Wonsik Kim187423c2014-06-25 14:12:48 +09002833
2834 @Override
2835 public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
2836 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002837 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
Wonsik Kim187423c2014-06-25 14:12:48 +09002838 // Broadcast the event to all hardware inputs.
2839 for (ServiceState serviceState : userState.serviceStateMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002840 if (!serviceState.isHardware || serviceState.service == null) continue;
Wonsik Kim187423c2014-06-25 14:12:48 +09002841 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002842 serviceState.service.notifyHardwareAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09002843 } catch (RemoteException e) {
2844 Slog.e(TAG, "error in notifyHardwareAdded", e);
2845 }
2846 }
2847 }
2848 }
2849
2850 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002851 public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
Wonsik Kim187423c2014-06-25 14:12:48 +09002852 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002853 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
Wonsik Kim187423c2014-06-25 14:12:48 +09002854 // Broadcast the event to all hardware inputs.
2855 for (ServiceState serviceState : userState.serviceStateMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002856 if (!serviceState.isHardware || serviceState.service == null) continue;
Wonsik Kim187423c2014-06-25 14:12:48 +09002857 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002858 serviceState.service.notifyHardwareRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09002859 } catch (RemoteException e) {
2860 Slog.e(TAG, "error in notifyHardwareRemoved", e);
2861 }
2862 }
2863 }
2864 }
2865
2866 @Override
Jae Seo546c6352014-08-07 11:57:01 -07002867 public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
Wonsik Kim187423c2014-06-25 14:12:48 +09002868 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002869 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002870 // Broadcast the event to all hardware inputs.
2871 for (ServiceState serviceState : userState.serviceStateMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002872 if (!serviceState.isHardware || serviceState.service == null) continue;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002873 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002874 serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002875 } catch (RemoteException e) {
Jae Seo546c6352014-08-07 11:57:01 -07002876 Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002877 }
2878 }
Wonsik Kim187423c2014-06-25 14:12:48 +09002879 }
2880 }
2881
2882 @Override
Jae Seo546c6352014-08-07 11:57:01 -07002883 public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
Wonsik Kim187423c2014-06-25 14:12:48 +09002884 synchronized (mLock) {
Jae Seo4f1a6d42015-07-20 16:15:01 -07002885 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002886 // Broadcast the event to all hardware inputs.
2887 for (ServiceState serviceState : userState.serviceStateMap.values()) {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002888 if (!serviceState.isHardware || serviceState.service == null) continue;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002889 try {
Dongwon Kangfd8aa022014-08-28 14:59:20 +09002890 serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002891 } catch (RemoteException e) {
Jae Seo546c6352014-08-07 11:57:01 -07002892 Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09002893 }
2894 }
Wonsik Kim187423c2014-06-25 14:12:48 +09002895 }
2896 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09002897
2898 @Override
Wonsik Kime92f8572014-08-12 18:30:24 +09002899 public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
2900 synchronized (mLock) {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002901 Integer state;
Wonsik Kime92f8572014-08-12 18:30:24 +09002902 switch (deviceInfo.getDevicePowerStatus()) {
2903 case HdmiControlManager.POWER_STATUS_ON:
2904 state = INPUT_STATE_CONNECTED;
2905 break;
2906 case HdmiControlManager.POWER_STATUS_STANDBY:
2907 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
2908 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
2909 state = INPUT_STATE_CONNECTED_STANDBY;
2910 break;
2911 case HdmiControlManager.POWER_STATUS_UNKNOWN:
2912 default:
2913 state = null;
2914 break;
2915 }
2916 if (state != null) {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07002917 setStateLocked(inputId, state, mCurrentUserId);
Wonsik Kime92f8572014-08-12 18:30:24 +09002918 }
2919 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09002920 }
Wonsik Kim969167d2014-06-24 16:33:17 +09002921 }
Dongwon Kangfdce9e52014-12-04 18:08:00 +09002922
2923 private static class SessionNotFoundException extends IllegalArgumentException {
Dongwon Kangfdce9e52014-12-04 18:08:00 +09002924 public SessionNotFoundException(String name) {
2925 super(name);
2926 }
2927 }
Jae Seo39570912014-02-20 18:23:25 -08002928}