TvInputManagerService.java revision 9c165d6e9a2f085fbdc87b9221f2d52d851b2652
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.tv;
18
19import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
20import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
21import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
22
23import android.app.ActivityManager;
24import android.content.BroadcastReceiver;
25import android.content.ComponentName;
26import android.content.ContentProviderOperation;
27import android.content.ContentProviderResult;
28import android.content.ContentResolver;
29import android.content.ContentUris;
30import android.content.ContentValues;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.OperationApplicationException;
35import android.content.ServiceConnection;
36import android.content.pm.ActivityInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.ResolveInfo;
39import android.content.pm.ServiceInfo;
40import android.graphics.Rect;
41import android.hardware.hdmi.HdmiControlManager;
42import android.hardware.hdmi.HdmiDeviceInfo;
43import android.media.tv.ITvInputClient;
44import android.media.tv.ITvInputHardware;
45import android.media.tv.ITvInputHardwareCallback;
46import android.media.tv.ITvInputManager;
47import android.media.tv.ITvInputManagerCallback;
48import android.media.tv.ITvInputService;
49import android.media.tv.ITvInputServiceCallback;
50import android.media.tv.ITvInputSession;
51import android.media.tv.ITvInputSessionCallback;
52import android.media.tv.TvContentRating;
53import android.media.tv.TvContentRatingSystemInfo;
54import android.media.tv.TvContract;
55import android.media.tv.TvInputHardwareInfo;
56import android.media.tv.TvInputInfo;
57import android.media.tv.TvInputManager;
58import android.media.tv.TvInputService;
59import android.media.tv.TvStreamConfig;
60import android.media.tv.TvTrackInfo;
61import android.net.Uri;
62import android.os.Binder;
63import android.os.Bundle;
64import android.os.Handler;
65import android.os.IBinder;
66import android.os.Looper;
67import android.os.Message;
68import android.os.Process;
69import android.os.RemoteException;
70import android.os.UserHandle;
71import android.util.Slog;
72import android.util.SparseArray;
73import android.view.InputChannel;
74import android.view.Surface;
75
76import com.android.internal.content.PackageMonitor;
77import com.android.internal.os.SomeArgs;
78import com.android.internal.util.IndentingPrintWriter;
79import com.android.server.IoThread;
80import com.android.server.SystemService;
81
82import org.xmlpull.v1.XmlPullParserException;
83
84import java.io.FileDescriptor;
85import java.io.IOException;
86import java.io.PrintWriter;
87import java.util.ArrayList;
88import java.util.HashMap;
89import java.util.HashSet;
90import java.util.Iterator;
91import java.util.List;
92import java.util.Map;
93import java.util.Set;
94
95/** This class provides a system service that manages television inputs. */
96public final class TvInputManagerService extends SystemService {
97    // STOPSHIP: Turn debugging off.
98    private static final boolean DEBUG = true;
99    private static final String TAG = "TvInputManagerService";
100
101    private final Context mContext;
102    private final TvInputHardwareManager mTvInputHardwareManager;
103
104    private final ContentResolver mContentResolver;
105
106    // A global lock.
107    private final Object mLock = new Object();
108
109    // ID of the current user.
110    private int mCurrentUserId = UserHandle.USER_OWNER;
111
112    // A map from user id to UserState.
113    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
114
115    private final WatchLogHandler mWatchLogHandler;
116
117    public TvInputManagerService(Context context) {
118        super(context);
119
120        mContext = context;
121        mContentResolver = context.getContentResolver();
122        mWatchLogHandler = new WatchLogHandler(IoThread.get().getLooper());
123
124        mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
125
126        synchronized (mLock) {
127            mUserStates.put(mCurrentUserId, new UserState(mContext, mCurrentUserId));
128        }
129    }
130
131    @Override
132    public void onStart() {
133        publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
134    }
135
136    @Override
137    public void onBootPhase(int phase) {
138        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
139            registerBroadcastReceivers();
140        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
141            synchronized (mLock) {
142                buildTvInputListLocked(mCurrentUserId);
143                buildTvContentRatingSystemListLocked(mCurrentUserId);
144            }
145        }
146        mTvInputHardwareManager.onBootPhase(phase);
147    }
148
149    private void registerBroadcastReceivers() {
150        PackageMonitor monitor = new PackageMonitor() {
151            @Override
152            public void onSomePackagesChanged() {
153                if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
154                synchronized (mLock) {
155                    buildTvInputListLocked(mCurrentUserId);
156                    buildTvContentRatingSystemListLocked(mCurrentUserId);
157                }
158            }
159
160            @Override
161            public void onPackageRemoved(String packageName, int uid) {
162                synchronized (mLock) {
163                    UserState userState = getUserStateLocked(mCurrentUserId);
164                    if (!userState.packageSet.contains(packageName)) {
165                        // Not a TV input package.
166                        return;
167                    }
168                }
169
170                ArrayList<ContentProviderOperation> operations =
171                        new ArrayList<ContentProviderOperation>();
172
173                String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?";
174                String[] selectionArgs = { packageName };
175
176                operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI)
177                        .withSelection(selection, selectionArgs).build());
178                operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI)
179                        .withSelection(selection, selectionArgs).build());
180                operations.add(ContentProviderOperation
181                        .newDelete(TvContract.WatchedPrograms.CONTENT_URI)
182                        .withSelection(selection, selectionArgs).build());
183
184                ContentProviderResult[] results = null;
185                try {
186                    results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
187                } catch (RemoteException | OperationApplicationException e) {
188                    Slog.e(TAG, "error in applyBatch", e);
189                }
190
191                if (DEBUG) {
192                    Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid
193                            + ")");
194                    Slog.d(TAG, "results=" + results);
195                }
196            }
197        };
198        monitor.register(mContext, null, UserHandle.ALL, true);
199
200        IntentFilter intentFilter = new IntentFilter();
201        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
202        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
203        mContext.registerReceiverAsUser(new BroadcastReceiver() {
204            @Override
205            public void onReceive(Context context, Intent intent) {
206                String action = intent.getAction();
207                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
208                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
209                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
210                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
211                }
212            }
213        }, UserHandle.ALL, intentFilter, null, null);
214    }
215
216    private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) {
217        return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE,
218                component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
219    }
220
221    private void buildTvInputListLocked(int userId) {
222        UserState userState = getUserStateLocked(userId);
223        userState.packageSet.clear();
224
225        if (DEBUG) {
226            Slog.d(TAG, "buildTvInputList");
227        }
228        PackageManager pm = mContext.getPackageManager();
229        List<ResolveInfo> services = pm.queryIntentServices(
230                new Intent(TvInputService.SERVICE_INTERFACE),
231                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
232        List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
233        for (ResolveInfo ri : services) {
234            ServiceInfo si = ri.serviceInfo;
235            if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
236                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
237                        + android.Manifest.permission.BIND_TV_INPUT);
238                continue;
239            }
240
241            ComponentName component = new ComponentName(si.packageName, si.name);
242            if (hasHardwarePermission(pm, component)) {
243                ServiceState serviceState = userState.serviceStateMap.get(component);
244                if (serviceState == null) {
245                    // We see this hardware TV input service for the first time; we need to
246                    // prepare the ServiceState object so that we can connect to the service and
247                    // let it add TvInputInfo objects to mInputList if there's any.
248                    serviceState = new ServiceState(component, userId);
249                    userState.serviceStateMap.put(component, serviceState);
250                } else {
251                    inputList.addAll(serviceState.mInputList);
252                }
253            } else {
254                try {
255                    inputList.add(TvInputInfo.createTvInputInfo(mContext, ri));
256                } catch (XmlPullParserException | IOException e) {
257                    Slog.e(TAG, "failed to load TV input " + si.name, e);
258                    continue;
259                }
260            }
261
262            // Reconnect the service if existing input is updated.
263            updateServiceConnectionLocked(component, userId);
264            userState.packageSet.add(si.packageName);
265        }
266
267        Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
268        for (TvInputInfo info : inputList) {
269            if (DEBUG) {
270                Slog.d(TAG, "add " + info.getId());
271            }
272            TvInputState state = userState.inputMap.get(info.getId());
273            if (state == null) {
274                state = new TvInputState();
275            }
276            state.mInfo = info;
277            inputMap.put(info.getId(), state);
278        }
279
280        for (String inputId : inputMap.keySet()) {
281            if (!userState.inputMap.containsKey(inputId)) {
282                notifyInputAddedLocked(userState, inputId);
283            }
284        }
285
286        for (String inputId : userState.inputMap.keySet()) {
287            if (!inputMap.containsKey(inputId)) {
288                TvInputInfo info = userState.inputMap.get(inputId).mInfo;
289                ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
290                if (serviceState != null) {
291                    abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
292                }
293                notifyInputRemovedLocked(userState, inputId);
294            }
295        }
296
297        userState.inputMap.clear();
298        userState.inputMap = inputMap;
299    }
300
301    private void buildTvContentRatingSystemListLocked(int userId) {
302        UserState userState = getUserStateLocked(userId);
303        userState.contentRatingSystemList.clear();
304
305        final PackageManager pm = mContext.getPackageManager();
306        Intent intent = new Intent(TvInputManager.ACTION_QUERY_CONTENT_RATING_SYSTEMS);
307        for (ResolveInfo resolveInfo :
308                pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA)) {
309            ActivityInfo receiver = resolveInfo.activityInfo;
310            Bundle metaData = receiver.metaData;
311            if (metaData == null) {
312                continue;
313            }
314
315            int xmlResId = metaData.getInt(TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS);
316            if (xmlResId == 0) {
317                Slog.w(TAG, "Missing meta-data '"
318                        + TvInputManager.META_DATA_CONTENT_RATING_SYSTEMS + "' on receiver "
319                        + receiver.packageName + "/" + receiver.name);
320                continue;
321            }
322            userState.contentRatingSystemList.add(
323                    TvContentRatingSystemInfo.createTvContentRatingSystemInfo(xmlResId,
324                            receiver.applicationInfo));
325        }
326    }
327
328    private void switchUser(int userId) {
329        synchronized (mLock) {
330            if (mCurrentUserId == userId) {
331                return;
332            }
333            // final int oldUserId = mCurrentUserId;
334            // TODO: Release services and sessions in the old user state, if needed.
335            mCurrentUserId = userId;
336
337            UserState userState = mUserStates.get(userId);
338            if (userState == null) {
339                userState = new UserState(mContext, userId);
340            }
341            mUserStates.put(userId, userState);
342            buildTvInputListLocked(userId);
343            buildTvContentRatingSystemListLocked(userId);
344        }
345    }
346
347    private void removeUser(int userId) {
348        synchronized (mLock) {
349            UserState userState = mUserStates.get(userId);
350            if (userState == null) {
351                return;
352            }
353            // Release created sessions.
354            for (SessionState state : userState.sessionStateMap.values()) {
355                if (state.mSession != null) {
356                    try {
357                        state.mSession.release();
358                    } catch (RemoteException e) {
359                        Slog.e(TAG, "error in release", e);
360                    }
361                }
362            }
363            userState.sessionStateMap.clear();
364
365            // Unregister all callbacks and unbind all services.
366            for (ServiceState serviceState : userState.serviceStateMap.values()) {
367                if (serviceState.mCallback != null) {
368                    try {
369                        serviceState.mService.unregisterCallback(serviceState.mCallback);
370                    } catch (RemoteException e) {
371                        Slog.e(TAG, "error in unregisterCallback", e);
372                    }
373                }
374                mContext.unbindService(serviceState.mConnection);
375            }
376            userState.serviceStateMap.clear();
377
378            // Clear everything else.
379            userState.inputMap.clear();
380            userState.packageSet.clear();
381            userState.contentRatingSystemList.clear();
382            userState.clientStateMap.clear();
383            userState.callbackSet.clear();
384            userState.mainSessionToken = null;
385
386            mUserStates.remove(userId);
387        }
388    }
389
390    private UserState getUserStateLocked(int userId) {
391        UserState userState = mUserStates.get(userId);
392        if (userState == null) {
393            throw new IllegalStateException("User state not found for user ID " + userId);
394        }
395        return userState;
396    }
397
398    private ServiceState getServiceStateLocked(ComponentName component, int userId) {
399        UserState userState = getUserStateLocked(userId);
400        ServiceState serviceState = userState.serviceStateMap.get(component);
401        if (serviceState == null) {
402            throw new IllegalStateException("Service state not found for " + component + " (userId="
403                    + userId + ")");
404        }
405        return serviceState;
406    }
407
408    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
409        UserState userState = getUserStateLocked(userId);
410        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
411        if (sessionState == null) {
412            throw new IllegalArgumentException("Session state not found for token " + sessionToken);
413        }
414        // Only the application that requested this session or the system can access it.
415        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
416            throw new SecurityException("Illegal access to the session with token " + sessionToken
417                    + " from uid " + callingUid);
418        }
419        return sessionState;
420    }
421
422    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
423        return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
424    }
425
426    private ITvInputSession getSessionLocked(SessionState sessionState) {
427        ITvInputSession session = sessionState.mSession;
428        if (session == null) {
429            throw new IllegalStateException("Session not yet created for token "
430                    + sessionState.mSessionToken);
431        }
432        return session;
433    }
434
435    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
436            String methodName) {
437        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
438                false, methodName, null);
439    }
440
441    private static boolean shouldMaintainConnection(ServiceState serviceState) {
442        return !serviceState.mSessionTokens.isEmpty() || serviceState.mIsHardware;
443        // TODO: Find a way to maintain connection only when necessary.
444    }
445
446    private void updateServiceConnectionLocked(ComponentName component, int userId) {
447        UserState userState = getUserStateLocked(userId);
448        ServiceState serviceState = userState.serviceStateMap.get(component);
449        if (serviceState == null) {
450            return;
451        }
452        if (serviceState.mReconnecting) {
453            if (!serviceState.mSessionTokens.isEmpty()) {
454                // wait until all the sessions are removed.
455                return;
456            }
457            serviceState.mReconnecting = false;
458        }
459        boolean maintainConnection = shouldMaintainConnection(serviceState);
460        if (serviceState.mService == null && maintainConnection && userId == mCurrentUserId) {
461            // This means that the service is not yet connected but its state indicates that we
462            // have pending requests. Then, connect the service.
463            if (serviceState.mBound) {
464                // We have already bound to the service so we don't try to bind again until after we
465                // unbind later on.
466                return;
467            }
468            if (DEBUG) {
469                Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
470            }
471
472            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
473            // Binding service may fail if the service is updating.
474            // In that case, the connection will be revived in buildTvInputListLocked called by
475            // onSomePackagesChanged.
476            serviceState.mBound = mContext.bindServiceAsUser(
477                    i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
478        } else if (serviceState.mService != null && !maintainConnection) {
479            // This means that the service is already connected but its state indicates that we have
480            // nothing to do with it. Then, disconnect the service.
481            if (DEBUG) {
482                Slog.d(TAG, "unbindService(service=" + component + ")");
483            }
484            mContext.unbindService(serviceState.mConnection);
485            userState.serviceStateMap.remove(component);
486        }
487    }
488
489    private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
490            String inputId, int userId) {
491        // Let clients know the create session requests are failed.
492        UserState userState = getUserStateLocked(userId);
493        List<SessionState> sessionsToAbort = new ArrayList<>();
494        for (IBinder sessionToken : serviceState.mSessionTokens) {
495            SessionState sessionState = userState.sessionStateMap.get(sessionToken);
496            if (sessionState.mSession == null && (inputId == null
497                    || sessionState.mInfo.getId().equals(inputId))) {
498                sessionsToAbort.add(sessionState);
499            }
500        }
501        for (SessionState sessionState : sessionsToAbort) {
502            removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
503            sendSessionTokenToClientLocked(sessionState.mClient,
504                    sessionState.mInfo.getId(), null, null, sessionState.mSeq);
505        }
506        updateServiceConnectionLocked(serviceState.mComponent, userId);
507    }
508
509    private ClientState createClientStateLocked(IBinder clientToken, int userId) {
510        UserState userState = getUserStateLocked(userId);
511        ClientState clientState = new ClientState(clientToken, userId);
512        try {
513            clientToken.linkToDeath(clientState, 0);
514        } catch (RemoteException e) {
515            Slog.e(TAG, "client process has already died", e);
516        }
517        userState.clientStateMap.put(clientToken, clientState);
518        return clientState;
519    }
520
521    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
522            final int userId) {
523        final UserState userState = getUserStateLocked(userId);
524        final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
525        if (DEBUG) {
526            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInfo.getId() + ")");
527        }
528
529        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
530
531        // Set up a callback to send the session token.
532        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
533            @Override
534            public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
535                if (DEBUG) {
536                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")");
537                }
538                synchronized (mLock) {
539                    sessionState.mSession = session;
540                    sessionState.mHardwareSessionToken = harewareSessionToken;
541                    if (session == null) {
542                        removeSessionStateLocked(sessionToken, userId);
543                        sendSessionTokenToClientLocked(sessionState.mClient,
544                                sessionState.mInfo.getId(), null, null, sessionState.mSeq);
545                    } else {
546                        try {
547                            session.asBinder().linkToDeath(sessionState, 0);
548                        } catch (RemoteException e) {
549                            Slog.e(TAG, "session process has already died", e);
550                        }
551
552                        IBinder clientToken = sessionState.mClient.asBinder();
553                        ClientState clientState = userState.clientStateMap.get(clientToken);
554                        if (clientState == null) {
555                            clientState = createClientStateLocked(clientToken, userId);
556                        }
557                        clientState.mSessionTokens.add(sessionState.mSessionToken);
558
559                        sendSessionTokenToClientLocked(sessionState.mClient,
560                                sessionState.mInfo.getId(), sessionToken, channels[0],
561                                sessionState.mSeq);
562                    }
563                    channels[0].dispose();
564                }
565            }
566
567            @Override
568            public void onChannelRetuned(Uri channelUri) {
569                synchronized (mLock) {
570                    if (DEBUG) {
571                        Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
572                    }
573                    if (sessionState.mSession == null || sessionState.mClient == null) {
574                        return;
575                    }
576                    try {
577                        // TODO: Consider adding this channel change in the watch log. When we do
578                        // that, how we can protect the watch log from malicious tv inputs should
579                        // be addressed. e.g. add a field which represents where the channel change
580                        // originated from.
581                        sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
582                    } catch (RemoteException e) {
583                        Slog.e(TAG, "error in onChannelRetuned", e);
584                    }
585                }
586            }
587
588            @Override
589            public void onTracksChanged(List<TvTrackInfo> tracks) {
590                synchronized (mLock) {
591                    if (DEBUG) {
592                        Slog.d(TAG, "onTracksChanged(" + tracks + ")");
593                    }
594                    if (sessionState.mSession == null || sessionState.mClient == null) {
595                        return;
596                    }
597                    try {
598                        sessionState.mClient.onTracksChanged(tracks, sessionState.mSeq);
599                    } catch (RemoteException e) {
600                        Slog.e(TAG, "error in onTracksChanged", e);
601                    }
602                }
603            }
604
605            @Override
606            public void onTrackSelected(int type, String trackId) {
607                synchronized (mLock) {
608                    if (DEBUG) {
609                        Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
610                    }
611                    if (sessionState.mSession == null || sessionState.mClient == null) {
612                        return;
613                    }
614                    try {
615                        sessionState.mClient.onTrackSelected(type, trackId, sessionState.mSeq);
616                    } catch (RemoteException e) {
617                        Slog.e(TAG, "error in onTrackSelected", e);
618                    }
619                }
620            }
621
622            @Override
623            public void onVideoAvailable() {
624                synchronized (mLock) {
625                    if (DEBUG) {
626                        Slog.d(TAG, "onVideoAvailable()");
627                    }
628                    if (sessionState.mSession == null || sessionState.mClient == null) {
629                        return;
630                    }
631                    try {
632                        sessionState.mClient.onVideoAvailable(sessionState.mSeq);
633                    } catch (RemoteException e) {
634                        Slog.e(TAG, "error in onVideoAvailable", e);
635                    }
636                }
637            }
638
639            @Override
640            public void onVideoUnavailable(int reason) {
641                synchronized (mLock) {
642                    if (DEBUG) {
643                        Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
644                    }
645                    if (sessionState.mSession == null || sessionState.mClient == null) {
646                        return;
647                    }
648                    try {
649                        sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq);
650                    } catch (RemoteException e) {
651                        Slog.e(TAG, "error in onVideoUnavailable", e);
652                    }
653                }
654            }
655
656            @Override
657            public void onContentAllowed() {
658                synchronized (mLock) {
659                    if (DEBUG) {
660                        Slog.d(TAG, "onContentAllowed()");
661                    }
662                    if (sessionState.mSession == null || sessionState.mClient == null) {
663                        return;
664                    }
665                    try {
666                        sessionState.mClient.onContentAllowed(sessionState.mSeq);
667                    } catch (RemoteException e) {
668                        Slog.e(TAG, "error in onContentAllowed", e);
669                    }
670                }
671            }
672
673            @Override
674            public void onContentBlocked(String rating) {
675                synchronized (mLock) {
676                    if (DEBUG) {
677                        Slog.d(TAG, "onContentBlocked()");
678                    }
679                    if (sessionState.mSession == null || sessionState.mClient == null) {
680                        return;
681                    }
682                    try {
683                        sessionState.mClient.onContentBlocked(rating, sessionState.mSeq);
684                    } catch (RemoteException e) {
685                        Slog.e(TAG, "error in onContentBlocked", e);
686                    }
687                }
688            }
689
690            @Override
691            public void onLayoutSurface(int left, int top, int right, int bottom) {
692                synchronized (mLock) {
693                    if (DEBUG) {
694                        Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
695                                + ", right=" + right + ", bottom=" + bottom + ",)");
696                    }
697                    if (sessionState.mSession == null || sessionState.mClient == null) {
698                        return;
699                    }
700                    try {
701                        sessionState.mClient.onLayoutSurface(left, top, right, bottom,
702                                sessionState.mSeq);
703                    } catch (RemoteException e) {
704                        Slog.e(TAG, "error in onLayoutSurface", e);
705                    }
706                }
707            }
708
709            @Override
710            public void onSessionEvent(String eventType, Bundle eventArgs) {
711                synchronized (mLock) {
712                    if (DEBUG) {
713                        Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
714                    }
715                    if (sessionState.mSession == null || sessionState.mClient == null) {
716                        return;
717                    }
718                    try {
719                        sessionState.mClient.onSessionEvent(eventType, eventArgs,
720                                sessionState.mSeq);
721                    } catch (RemoteException e) {
722                        Slog.e(TAG, "error in onSessionEvent", e);
723                    }
724                }
725            }
726        };
727
728        // Create a session. When failed, send a null token immediately.
729        try {
730            service.createSession(channels[1], callback, sessionState.mInfo.getId());
731        } catch (RemoteException e) {
732            Slog.e(TAG, "error in createSession", e);
733            removeSessionStateLocked(sessionToken, userId);
734            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInfo.getId(), null,
735                    null, sessionState.mSeq);
736        }
737        channels[1].dispose();
738    }
739
740    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
741            IBinder sessionToken, InputChannel channel, int seq) {
742        try {
743            client.onSessionCreated(inputId, sessionToken, channel, seq);
744        } catch (RemoteException e) {
745            Slog.e(TAG, "error in onSessionCreated", e);
746        }
747    }
748
749    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
750        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
751        if (sessionState.mSession != null) {
752            UserState userState = getUserStateLocked(userId);
753            if (sessionToken == userState.mainSessionToken) {
754                setMainLocked(sessionToken, false, callingUid, userId);
755            }
756            try {
757                sessionState.mSession.release();
758            } catch (RemoteException e) {
759                Slog.e(TAG, "session process has already died", e);
760            }
761            sessionState.mSession = null;
762        }
763        removeSessionStateLocked(sessionToken, userId);
764    }
765
766    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
767        UserState userState = getUserStateLocked(userId);
768        if (sessionToken == userState.mainSessionToken) {
769            if (DEBUG) {
770                Slog.d(TAG, "mainSessionToken=null");
771            }
772            userState.mainSessionToken = null;
773        }
774
775        // Remove the session state from the global session state map of the current user.
776        SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
777
778        if (sessionState == null) {
779            return;
780        }
781
782        // Also remove the session token from the session token list of the current client and
783        // service.
784        ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
785        if (clientState != null) {
786            clientState.mSessionTokens.remove(sessionToken);
787            if (clientState.isEmpty()) {
788                userState.clientStateMap.remove(sessionState.mClient.asBinder());
789            }
790        }
791
792        TvInputInfo info = sessionState.mInfo;
793        if (info != null) {
794            ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
795            if (serviceState != null) {
796                serviceState.mSessionTokens.remove(sessionToken);
797            }
798        }
799        updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId);
800
801        // Log the end of watch.
802        SomeArgs args = SomeArgs.obtain();
803        args.arg1 = sessionToken;
804        args.arg2 = System.currentTimeMillis();
805        mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_END, args).sendToTarget();
806    }
807
808    private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
809        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
810        if (sessionState.mHardwareSessionToken != null) {
811            sessionState = getSessionStateLocked(sessionState.mHardwareSessionToken,
812                    Process.SYSTEM_UID, userId);
813        }
814        ServiceState serviceState = getServiceStateLocked(sessionState.mInfo.getComponent(),
815                userId);
816        if (!serviceState.mIsHardware) {
817            return;
818        }
819        ITvInputSession session = getSessionLocked(sessionState);
820        try {
821            session.setMain(isMain);
822        } catch (RemoteException e) {
823            Slog.e(TAG, "error in setMain", e);
824        }
825    }
826
827    private void notifyInputAddedLocked(UserState userState, String inputId) {
828        if (DEBUG) {
829            Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")");
830        }
831        for (ITvInputManagerCallback callback : userState.callbackSet) {
832            try {
833                callback.onInputAdded(inputId);
834            } catch (RemoteException e) {
835                Slog.e(TAG, "failed to report added input to callback", e);
836            }
837        }
838    }
839
840    private void notifyInputRemovedLocked(UserState userState, String inputId) {
841        if (DEBUG) {
842            Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")");
843        }
844        for (ITvInputManagerCallback callback : userState.callbackSet) {
845            try {
846                callback.onInputRemoved(inputId);
847            } catch (RemoteException e) {
848                Slog.e(TAG, "failed to report removed input to callback", e);
849            }
850        }
851    }
852
853    private void notifyInputStateChangedLocked(UserState userState, String inputId,
854            int state, ITvInputManagerCallback targetCallback) {
855        if (DEBUG) {
856            Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId
857                    + ", state=" + state + ")");
858        }
859        if (targetCallback == null) {
860            for (ITvInputManagerCallback callback : userState.callbackSet) {
861                try {
862                    callback.onInputStateChanged(inputId, state);
863                } catch (RemoteException e) {
864                    Slog.e(TAG, "failed to report state change to callback", e);
865                }
866            }
867        } else {
868            try {
869                targetCallback.onInputStateChanged(inputId, state);
870            } catch (RemoteException e) {
871                Slog.e(TAG, "failed to report state change to callback", e);
872            }
873        }
874    }
875
876    private void setStateLocked(String inputId, int state, int userId) {
877        UserState userState = getUserStateLocked(userId);
878        TvInputState inputState = userState.inputMap.get(inputId);
879        ServiceState serviceState = userState.serviceStateMap.get(inputState.mInfo.getComponent());
880        int oldState = inputState.mState;
881        inputState.mState = state;
882        if (serviceState != null && serviceState.mService == null
883                && shouldMaintainConnection(serviceState)) {
884            // We don't notify state change while reconnecting. It should remain disconnected.
885            return;
886        }
887        if (oldState != state) {
888            notifyInputStateChangedLocked(userState, inputId, state, null);
889        }
890    }
891
892    private final class BinderService extends ITvInputManager.Stub {
893        @Override
894        public List<TvInputInfo> getTvInputList(int userId) {
895            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
896                    Binder.getCallingUid(), userId, "getTvInputList");
897            final long identity = Binder.clearCallingIdentity();
898            try {
899                synchronized (mLock) {
900                    UserState userState = getUserStateLocked(resolvedUserId);
901                    List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
902                    for (TvInputState state : userState.inputMap.values()) {
903                        inputList.add(state.mInfo);
904                    }
905                    return inputList;
906                }
907            } finally {
908                Binder.restoreCallingIdentity(identity);
909            }
910        }
911
912        @Override
913        public TvInputInfo getTvInputInfo(String inputId, int userId) {
914            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
915                    Binder.getCallingUid(), userId, "getTvInputInfo");
916            final long identity = Binder.clearCallingIdentity();
917            try {
918                synchronized (mLock) {
919                    UserState userState = getUserStateLocked(resolvedUserId);
920                    TvInputState state = userState.inputMap.get(inputId);
921                    return state == null ? null : state.mInfo;
922                }
923            } finally {
924                Binder.restoreCallingIdentity(identity);
925            }
926        }
927
928        @Override
929        public List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId) {
930            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
931                    Binder.getCallingUid(), userId, "getTvContentRatingSystemList");
932            final long identity = Binder.clearCallingIdentity();
933            try {
934                synchronized (mLock) {
935                    UserState userState = getUserStateLocked(resolvedUserId);
936                    return userState.contentRatingSystemList;
937                }
938            } finally {
939                Binder.restoreCallingIdentity(identity);
940            }
941        }
942
943        @Override
944        public void registerCallback(final ITvInputManagerCallback callback, int userId) {
945            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
946                    Binder.getCallingUid(), userId, "registerCallback");
947            final long identity = Binder.clearCallingIdentity();
948            try {
949                synchronized (mLock) {
950                    final UserState userState = getUserStateLocked(resolvedUserId);
951                    userState.callbackSet.add(callback);
952                    try {
953                        callback.asBinder().linkToDeath(new IBinder.DeathRecipient() {
954                            @Override
955                            public void binderDied() {
956                                synchronized (mLock) {
957                                    if (userState.callbackSet != null) {
958                                        userState.callbackSet.remove(callback);
959                                    }
960                                }
961                            }
962                        }, 0);
963                    } catch (RemoteException e) {
964                        Slog.e(TAG, "client process has already died", e);
965                    }
966                    for (TvInputState state : userState.inputMap.values()) {
967                        notifyInputStateChangedLocked(userState, state.mInfo.getId(),
968                                state.mState, callback);
969                    }
970                }
971            } finally {
972                Binder.restoreCallingIdentity(identity);
973            }
974        }
975
976        @Override
977        public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
978            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
979                    Binder.getCallingUid(), userId, "unregisterCallback");
980            final long identity = Binder.clearCallingIdentity();
981            try {
982                synchronized (mLock) {
983                    UserState userState = getUserStateLocked(resolvedUserId);
984                    userState.callbackSet.remove(callback);
985                }
986            } finally {
987                Binder.restoreCallingIdentity(identity);
988            }
989        }
990
991        @Override
992        public boolean isParentalControlsEnabled(int userId) {
993            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
994                    Binder.getCallingUid(), userId, "isParentalControlsEnabled");
995            final long identity = Binder.clearCallingIdentity();
996            try {
997                synchronized (mLock) {
998                    UserState userState = getUserStateLocked(resolvedUserId);
999                    return userState.persistentDataStore.isParentalControlsEnabled();
1000                }
1001            } finally {
1002                Binder.restoreCallingIdentity(identity);
1003            }
1004        }
1005
1006        @Override
1007        public void setParentalControlsEnabled(boolean enabled, int userId) {
1008            ensureParentalControlsPermission();
1009            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1010                    Binder.getCallingUid(), userId, "setParentalControlsEnabled");
1011            final long identity = Binder.clearCallingIdentity();
1012            try {
1013                synchronized (mLock) {
1014                    UserState userState = getUserStateLocked(resolvedUserId);
1015                    userState.persistentDataStore.setParentalControlsEnabled(enabled);
1016                }
1017            } finally {
1018                Binder.restoreCallingIdentity(identity);
1019            }
1020        }
1021
1022        @Override
1023        public boolean isRatingBlocked(String rating, int userId) {
1024            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1025                    Binder.getCallingUid(), userId, "isRatingBlocked");
1026            final long identity = Binder.clearCallingIdentity();
1027            try {
1028                synchronized (mLock) {
1029                    UserState userState = getUserStateLocked(resolvedUserId);
1030                    return userState.persistentDataStore.isRatingBlocked(
1031                            TvContentRating.unflattenFromString(rating));
1032                }
1033            } finally {
1034                Binder.restoreCallingIdentity(identity);
1035            }
1036        }
1037
1038        @Override
1039        public List<String> getBlockedRatings(int userId) {
1040            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1041                    Binder.getCallingUid(), userId, "getBlockedRatings");
1042            final long identity = Binder.clearCallingIdentity();
1043            try {
1044                synchronized (mLock) {
1045                    UserState userState = getUserStateLocked(resolvedUserId);
1046                    List<String> ratings = new ArrayList<String>();
1047                    for (TvContentRating rating
1048                            : userState.persistentDataStore.getBlockedRatings()) {
1049                        ratings.add(rating.flattenToString());
1050                    }
1051                    return ratings;
1052                }
1053            } finally {
1054                Binder.restoreCallingIdentity(identity);
1055            }
1056        }
1057
1058        @Override
1059        public void addBlockedRating(String rating, int userId) {
1060            ensureParentalControlsPermission();
1061            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1062                    Binder.getCallingUid(), userId, "addBlockedRating");
1063            final long identity = Binder.clearCallingIdentity();
1064            try {
1065                synchronized (mLock) {
1066                    UserState userState = getUserStateLocked(resolvedUserId);
1067                    userState.persistentDataStore.addBlockedRating(
1068                            TvContentRating.unflattenFromString(rating));
1069                }
1070            } finally {
1071                Binder.restoreCallingIdentity(identity);
1072            }
1073        }
1074
1075        @Override
1076        public void removeBlockedRating(String rating, int userId) {
1077            ensureParentalControlsPermission();
1078            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
1079                    Binder.getCallingUid(), userId, "removeBlockedRating");
1080            final long identity = Binder.clearCallingIdentity();
1081            try {
1082                synchronized (mLock) {
1083                    UserState userState = getUserStateLocked(resolvedUserId);
1084                    userState.persistentDataStore.removeBlockedRating(
1085                            TvContentRating.unflattenFromString(rating));
1086                }
1087            } finally {
1088                Binder.restoreCallingIdentity(identity);
1089            }
1090        }
1091
1092        private void ensureParentalControlsPermission() {
1093            if (mContext.checkCallingPermission(
1094                    android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
1095                    != PackageManager.PERMISSION_GRANTED) {
1096                throw new SecurityException(
1097                        "The caller does not have parental controls permission");
1098            }
1099        }
1100
1101        @Override
1102        public void createSession(final ITvInputClient client, final String inputId,
1103                int seq, int userId) {
1104            final int callingUid = Binder.getCallingUid();
1105            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1106                    userId, "createSession");
1107            final long identity = Binder.clearCallingIdentity();
1108            try {
1109                synchronized (mLock) {
1110                    UserState userState = getUserStateLocked(resolvedUserId);
1111                    TvInputState inputState = userState.inputMap.get(inputId);
1112                    if (inputState == null) {
1113                        Slog.w(TAG, "Failed to find input state for inputId=" + inputId);
1114                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1115                        return;
1116                    }
1117                    TvInputInfo info = inputState.mInfo;
1118                    ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
1119                    if (serviceState == null) {
1120                        serviceState = new ServiceState(info.getComponent(), resolvedUserId);
1121                        userState.serviceStateMap.put(info.getComponent(), serviceState);
1122                    }
1123                    // Send a null token immediately while reconnecting.
1124                    if (serviceState.mReconnecting == true) {
1125                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
1126                        return;
1127                    }
1128
1129                    // Create a new session token and a session state.
1130                    IBinder sessionToken = new Binder();
1131                    SessionState sessionState = new SessionState(sessionToken, info, client,
1132                            seq, callingUid, resolvedUserId);
1133
1134                    // Add them to the global session state map of the current user.
1135                    userState.sessionStateMap.put(sessionToken, sessionState);
1136
1137                    // Also, add them to the session state map of the current service.
1138                    serviceState.mSessionTokens.add(sessionToken);
1139
1140                    if (serviceState.mService != null) {
1141                        createSessionInternalLocked(serviceState.mService, sessionToken,
1142                                resolvedUserId);
1143                    } else {
1144                        updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
1145                    }
1146                }
1147            } finally {
1148                Binder.restoreCallingIdentity(identity);
1149            }
1150        }
1151
1152        @Override
1153        public void releaseSession(IBinder sessionToken, int userId) {
1154            if (DEBUG) {
1155                Slog.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
1156            }
1157            final int callingUid = Binder.getCallingUid();
1158            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1159                    userId, "releaseSession");
1160            final long identity = Binder.clearCallingIdentity();
1161            try {
1162                synchronized (mLock) {
1163                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
1164                }
1165            } finally {
1166                Binder.restoreCallingIdentity(identity);
1167            }
1168        }
1169
1170        @Override
1171        public void setMainSession(IBinder sessionToken, int userId) {
1172            if (DEBUG) {
1173                Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")");
1174            }
1175            final int callingUid = Binder.getCallingUid();
1176            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1177                    userId, "setMainSession");
1178            final long identity = Binder.clearCallingIdentity();
1179            try {
1180                synchronized (mLock) {
1181                    UserState userState = getUserStateLocked(resolvedUserId);
1182                    if (userState.mainSessionToken == sessionToken) {
1183                        return;
1184                    }
1185                    if (DEBUG) {
1186                        Slog.d(TAG, "mainSessionToken=" + sessionToken);
1187                    }
1188                    IBinder oldMainSessionToken = userState.mainSessionToken;
1189                    userState.mainSessionToken = sessionToken;
1190
1191                    // Inform the new main session first.
1192                    // See {@link TvInputService.Session#onSetMain}.
1193                    if (sessionToken != null) {
1194                        setMainLocked(sessionToken, true, callingUid, userId);
1195                    }
1196                    if (oldMainSessionToken != null) {
1197                        setMainLocked(oldMainSessionToken, false, Process.SYSTEM_UID, userId);
1198                    }
1199                }
1200            } finally {
1201                Binder.restoreCallingIdentity(identity);
1202            }
1203        }
1204
1205        @Override
1206        public void setSurface(IBinder sessionToken, Surface surface, int userId) {
1207            final int callingUid = Binder.getCallingUid();
1208            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1209                    userId, "setSurface");
1210            final long identity = Binder.clearCallingIdentity();
1211            try {
1212                synchronized (mLock) {
1213                    try {
1214                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1215                                resolvedUserId);
1216                        if (sessionState.mHardwareSessionToken == null) {
1217                            getSessionLocked(sessionState).setSurface(surface);
1218                        } else {
1219                            getSessionLocked(sessionState.mHardwareSessionToken,
1220                                    Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
1221                        }
1222                    } catch (RemoteException e) {
1223                        Slog.e(TAG, "error in setSurface", e);
1224                    }
1225                }
1226            } finally {
1227                if (surface != null) {
1228                    // surface is not used in TvInputManagerService.
1229                    surface.release();
1230                }
1231                Binder.restoreCallingIdentity(identity);
1232            }
1233        }
1234
1235        @Override
1236        public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
1237                int height, int userId) {
1238            final int callingUid = Binder.getCallingUid();
1239            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1240                    userId, "dispatchSurfaceChanged");
1241            final long identity = Binder.clearCallingIdentity();
1242            try {
1243                synchronized (mLock) {
1244                    try {
1245                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1246                                resolvedUserId);
1247                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, height);
1248                        if (sessionState.mHardwareSessionToken != null) {
1249                            getSessionLocked(sessionState.mHardwareSessionToken, Process.SYSTEM_UID,
1250                                    resolvedUserId).dispatchSurfaceChanged(format, width, height);
1251                        }
1252                    } catch (RemoteException e) {
1253                        Slog.e(TAG, "error in dispatchSurfaceChanged", e);
1254                    }
1255                }
1256            } finally {
1257                Binder.restoreCallingIdentity(identity);
1258            }
1259        }
1260
1261        @Override
1262        public void setVolume(IBinder sessionToken, float volume, int userId) {
1263            final float REMOTE_VOLUME_ON = 1.0f;
1264            final float REMOTE_VOLUME_OFF = 0f;
1265            final int callingUid = Binder.getCallingUid();
1266            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1267                    userId, "setVolume");
1268            final long identity = Binder.clearCallingIdentity();
1269            try {
1270                synchronized (mLock) {
1271                    try {
1272                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
1273                                resolvedUserId);
1274                        getSessionLocked(sessionState).setVolume(volume);
1275                        if (sessionState.mHardwareSessionToken != null) {
1276                            // Here, we let the hardware session know only whether volume is on or
1277                            // off to prevent that the volume is controlled in the both side.
1278                            getSessionLocked(sessionState.mHardwareSessionToken,
1279                                    Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
1280                                            ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
1281                        }
1282                    } catch (RemoteException e) {
1283                        Slog.e(TAG, "error in setVolume", e);
1284                    }
1285                }
1286            } finally {
1287                Binder.restoreCallingIdentity(identity);
1288            }
1289        }
1290
1291        @Override
1292        public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
1293            final int callingUid = Binder.getCallingUid();
1294            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1295                    userId, "tune");
1296            final long identity = Binder.clearCallingIdentity();
1297            try {
1298                synchronized (mLock) {
1299                    try {
1300                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
1301                                channelUri, params);
1302                        if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
1303                            // Do not log the watch history for passthrough inputs.
1304                            return;
1305                        }
1306
1307                        UserState userState = getUserStateLocked(resolvedUserId);
1308                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
1309
1310                        // Log the start of watch.
1311                        SomeArgs args = SomeArgs.obtain();
1312                        args.arg1 = sessionState.mInfo.getComponent().getPackageName();
1313                        args.arg2 = System.currentTimeMillis();
1314                        args.arg3 = ContentUris.parseId(channelUri);
1315                        args.arg4 = params;
1316                        args.arg5 = sessionToken;
1317                        mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_LOG_WATCH_START, args)
1318                                .sendToTarget();
1319                    } catch (RemoteException e) {
1320                        Slog.e(TAG, "error in tune", e);
1321                        return;
1322                    }
1323                }
1324            } finally {
1325                Binder.restoreCallingIdentity(identity);
1326            }
1327        }
1328
1329        @Override
1330        public void requestUnblockContent(
1331                IBinder sessionToken, String unblockedRating, int userId) {
1332            final int callingUid = Binder.getCallingUid();
1333            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1334                    userId, "unblockContent");
1335            final long identity = Binder.clearCallingIdentity();
1336            try {
1337                synchronized (mLock) {
1338                    try {
1339                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1340                                .requestUnblockContent(unblockedRating);
1341                    } catch (RemoteException e) {
1342                        Slog.e(TAG, "error in requestUnblockContent", e);
1343                    }
1344                }
1345            } finally {
1346                Binder.restoreCallingIdentity(identity);
1347            }
1348        }
1349
1350        @Override
1351        public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
1352            final int callingUid = Binder.getCallingUid();
1353            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1354                    userId, "setCaptionEnabled");
1355            final long identity = Binder.clearCallingIdentity();
1356            try {
1357                synchronized (mLock) {
1358                    try {
1359                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1360                                .setCaptionEnabled(enabled);
1361                    } catch (RemoteException e) {
1362                        Slog.e(TAG, "error in setCaptionEnabled", e);
1363                    }
1364                }
1365            } finally {
1366                Binder.restoreCallingIdentity(identity);
1367            }
1368        }
1369
1370        @Override
1371        public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
1372            final int callingUid = Binder.getCallingUid();
1373            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1374                    userId, "selectTrack");
1375            final long identity = Binder.clearCallingIdentity();
1376            try {
1377                synchronized (mLock) {
1378                    try {
1379                        getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
1380                                type, trackId);
1381                    } catch (RemoteException e) {
1382                        Slog.e(TAG, "error in selectTrack", e);
1383                    }
1384                }
1385            } finally {
1386                Binder.restoreCallingIdentity(identity);
1387            }
1388        }
1389
1390        @Override
1391        public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
1392                int userId) {
1393            final int callingUid = Binder.getCallingUid();
1394            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1395                    userId, "sendAppPrivateCommand");
1396            final long identity = Binder.clearCallingIdentity();
1397            try {
1398                synchronized (mLock) {
1399                    try {
1400                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1401                                .appPrivateCommand(command, data);
1402                    } catch (RemoteException e) {
1403                        Slog.e(TAG, "error in appPrivateCommand", e);
1404                    }
1405                }
1406            } finally {
1407                Binder.restoreCallingIdentity(identity);
1408            }
1409        }
1410
1411        @Override
1412        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
1413                int userId) {
1414            final int callingUid = Binder.getCallingUid();
1415            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1416                    userId, "createOverlayView");
1417            final long identity = Binder.clearCallingIdentity();
1418            try {
1419                synchronized (mLock) {
1420                    try {
1421                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1422                                .createOverlayView(windowToken, frame);
1423                    } catch (RemoteException e) {
1424                        Slog.e(TAG, "error in createOverlayView", e);
1425                    }
1426                }
1427            } finally {
1428                Binder.restoreCallingIdentity(identity);
1429            }
1430        }
1431
1432        @Override
1433        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
1434            final int callingUid = Binder.getCallingUid();
1435            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1436                    userId, "relayoutOverlayView");
1437            final long identity = Binder.clearCallingIdentity();
1438            try {
1439                synchronized (mLock) {
1440                    try {
1441                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1442                                .relayoutOverlayView(frame);
1443                    } catch (RemoteException e) {
1444                        Slog.e(TAG, "error in relayoutOverlayView", e);
1445                    }
1446                }
1447            } finally {
1448                Binder.restoreCallingIdentity(identity);
1449            }
1450        }
1451
1452        @Override
1453        public void removeOverlayView(IBinder sessionToken, int userId) {
1454            final int callingUid = Binder.getCallingUid();
1455            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1456                    userId, "removeOverlayView");
1457            final long identity = Binder.clearCallingIdentity();
1458            try {
1459                synchronized (mLock) {
1460                    try {
1461                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
1462                                .removeOverlayView();
1463                    } catch (RemoteException e) {
1464                        Slog.e(TAG, "error in removeOverlayView", e);
1465                    }
1466                }
1467            } finally {
1468                Binder.restoreCallingIdentity(identity);
1469            }
1470        }
1471
1472        @Override
1473        public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
1474            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1475                    != PackageManager.PERMISSION_GRANTED) {
1476                return null;
1477            }
1478
1479            final long identity = Binder.clearCallingIdentity();
1480            try {
1481                return mTvInputHardwareManager.getHardwareList();
1482            } finally {
1483                Binder.restoreCallingIdentity(identity);
1484            }
1485        }
1486
1487        @Override
1488        public ITvInputHardware acquireTvInputHardware(int deviceId,
1489                ITvInputHardwareCallback callback, TvInputInfo info, int userId)
1490                throws RemoteException {
1491            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1492                    != PackageManager.PERMISSION_GRANTED) {
1493                return null;
1494            }
1495
1496            final long identity = Binder.clearCallingIdentity();
1497            final int callingUid = Binder.getCallingUid();
1498            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1499                    userId, "acquireTvInputHardware");
1500            try {
1501                return mTvInputHardwareManager.acquireHardware(
1502                        deviceId, callback, info, callingUid, resolvedUserId);
1503            } finally {
1504                Binder.restoreCallingIdentity(identity);
1505            }
1506        }
1507
1508        @Override
1509        public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
1510                throws RemoteException {
1511            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
1512                    != PackageManager.PERMISSION_GRANTED) {
1513                return;
1514            }
1515
1516            final long identity = Binder.clearCallingIdentity();
1517            final int callingUid = Binder.getCallingUid();
1518            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1519                    userId, "releaseTvInputHardware");
1520            try {
1521                mTvInputHardwareManager.releaseHardware(
1522                        deviceId, hardware, callingUid, resolvedUserId);
1523            } finally {
1524                Binder.restoreCallingIdentity(identity);
1525            }
1526        }
1527
1528        @Override
1529        public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
1530                throws RemoteException {
1531            if (mContext.checkCallingPermission(
1532                    android.Manifest.permission.CAPTURE_TV_INPUT)
1533                    != PackageManager.PERMISSION_GRANTED) {
1534                throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1535            }
1536
1537            final long identity = Binder.clearCallingIdentity();
1538            final int callingUid = Binder.getCallingUid();
1539            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1540                    userId, "getAvailableTvStreamConfigList");
1541            try {
1542                return mTvInputHardwareManager.getAvailableTvStreamConfigList(
1543                        inputId, callingUid, resolvedUserId);
1544            } finally {
1545                Binder.restoreCallingIdentity(identity);
1546            }
1547        }
1548
1549        @Override
1550        public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
1551                int userId)
1552                throws RemoteException {
1553            if (mContext.checkCallingPermission(
1554                    android.Manifest.permission.CAPTURE_TV_INPUT)
1555                    != PackageManager.PERMISSION_GRANTED) {
1556                throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
1557            }
1558
1559            final long identity = Binder.clearCallingIdentity();
1560            final int callingUid = Binder.getCallingUid();
1561            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1562                    userId, "captureFrame");
1563            try {
1564                String hardwareInputId = null;
1565                synchronized (mLock) {
1566                    UserState userState = getUserStateLocked(resolvedUserId);
1567                    if (userState.inputMap.get(inputId) == null) {
1568                        Slog.e(TAG, "input not found for " + inputId);
1569                        return false;
1570                    }
1571                    for (SessionState sessionState : userState.sessionStateMap.values()) {
1572                        if (sessionState.mInfo.getId().equals(inputId)
1573                                && sessionState.mHardwareSessionToken != null) {
1574                            hardwareInputId = userState.sessionStateMap.get(
1575                                    sessionState.mHardwareSessionToken).mInfo.getId();
1576                            break;
1577                        }
1578                    }
1579                }
1580                return mTvInputHardwareManager.captureFrame(
1581                        (hardwareInputId != null) ? hardwareInputId : inputId,
1582                        surface, config, callingUid, resolvedUserId);
1583            } finally {
1584                Binder.restoreCallingIdentity(identity);
1585            }
1586        }
1587
1588        @Override
1589        public boolean isSingleSessionActive(int userId) throws RemoteException {
1590            final long identity = Binder.clearCallingIdentity();
1591            final int callingUid = Binder.getCallingUid();
1592            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
1593                    userId, "isSingleSessionActive");
1594            try {
1595                synchronized (mLock) {
1596                    UserState userState = getUserStateLocked(resolvedUserId);
1597                    if (userState.sessionStateMap.size() == 1) {
1598                        return true;
1599                    }
1600                    else if (userState.sessionStateMap.size() == 2) {
1601                        SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
1602                                new SessionState[0]);
1603                        // Check if there is a wrapper input.
1604                        if (sessionStates[0].mHardwareSessionToken != null
1605                                || sessionStates[1].mHardwareSessionToken != null) {
1606                            return true;
1607                        }
1608                    }
1609                    return false;
1610                }
1611            } finally {
1612                Binder.restoreCallingIdentity(identity);
1613            }
1614        }
1615
1616        @Override
1617        @SuppressWarnings("resource")
1618        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1619            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
1620            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1621                    != PackageManager.PERMISSION_GRANTED) {
1622                pw.println("Permission Denial: can't dump TvInputManager from pid="
1623                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
1624                return;
1625            }
1626
1627            synchronized (mLock) {
1628                pw.println("User Ids (Current user: " + mCurrentUserId + "):");
1629                pw.increaseIndent();
1630                for (int i = 0; i < mUserStates.size(); i++) {
1631                    int userId = mUserStates.keyAt(i);
1632                    pw.println(Integer.valueOf(userId));
1633                }
1634                pw.decreaseIndent();
1635
1636                for (int i = 0; i < mUserStates.size(); i++) {
1637                    int userId = mUserStates.keyAt(i);
1638                    UserState userState = getUserStateLocked(userId);
1639                    pw.println("UserState (" + userId + "):");
1640                    pw.increaseIndent();
1641
1642                    pw.println("inputMap: inputId -> TvInputState");
1643                    pw.increaseIndent();
1644                    for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) {
1645                        pw.println(entry.getKey() + ": " + entry.getValue());
1646                    }
1647                    pw.decreaseIndent();
1648
1649                    pw.println("packageSet:");
1650                    pw.increaseIndent();
1651                    for (String packageName : userState.packageSet) {
1652                        pw.println(packageName);
1653                    }
1654                    pw.decreaseIndent();
1655
1656                    pw.println("clientStateMap: ITvInputClient -> ClientState");
1657                    pw.increaseIndent();
1658                    for (Map.Entry<IBinder, ClientState> entry :
1659                            userState.clientStateMap.entrySet()) {
1660                        ClientState client = entry.getValue();
1661                        pw.println(entry.getKey() + ": " + client);
1662
1663                        pw.increaseIndent();
1664
1665                        pw.println("mSessionTokens:");
1666                        pw.increaseIndent();
1667                        for (IBinder token : client.mSessionTokens) {
1668                            pw.println("" + token);
1669                        }
1670                        pw.decreaseIndent();
1671
1672                        pw.println("mClientTokens: " + client.mClientToken);
1673                        pw.println("mUserId: " + client.mUserId);
1674
1675                        pw.decreaseIndent();
1676                    }
1677                    pw.decreaseIndent();
1678
1679                    pw.println("serviceStateMap: ComponentName -> ServiceState");
1680                    pw.increaseIndent();
1681                    for (Map.Entry<ComponentName, ServiceState> entry :
1682                            userState.serviceStateMap.entrySet()) {
1683                        ServiceState service = entry.getValue();
1684                        pw.println(entry.getKey() + ": " + service);
1685
1686                        pw.increaseIndent();
1687
1688                        pw.println("mSessionTokens:");
1689                        pw.increaseIndent();
1690                        for (IBinder token : service.mSessionTokens) {
1691                            pw.println("" + token);
1692                        }
1693                        pw.decreaseIndent();
1694
1695                        pw.println("mService: " + service.mService);
1696                        pw.println("mCallback: " + service.mCallback);
1697                        pw.println("mBound: " + service.mBound);
1698                        pw.println("mReconnecting: " + service.mReconnecting);
1699
1700                        pw.decreaseIndent();
1701                    }
1702                    pw.decreaseIndent();
1703
1704                    pw.println("sessionStateMap: ITvInputSession -> SessionState");
1705                    pw.increaseIndent();
1706                    for (Map.Entry<IBinder, SessionState> entry :
1707                            userState.sessionStateMap.entrySet()) {
1708                        SessionState session = entry.getValue();
1709                        pw.println(entry.getKey() + ": " + session);
1710
1711                        pw.increaseIndent();
1712                        pw.println("mInfo: " + session.mInfo);
1713                        pw.println("mClient: " + session.mClient);
1714                        pw.println("mSeq: " + session.mSeq);
1715                        pw.println("mCallingUid: " + session.mCallingUid);
1716                        pw.println("mUserId: " + session.mUserId);
1717                        pw.println("mSessionToken: " + session.mSessionToken);
1718                        pw.println("mSession: " + session.mSession);
1719                        pw.println("mLogUri: " + session.mLogUri);
1720                        pw.println("mHardwareSessionToken: " + session.mHardwareSessionToken);
1721                        pw.decreaseIndent();
1722                    }
1723                    pw.decreaseIndent();
1724
1725                    pw.println("callbackSet:");
1726                    pw.increaseIndent();
1727                    for (ITvInputManagerCallback callback : userState.callbackSet) {
1728                        pw.println(callback.toString());
1729                    }
1730                    pw.decreaseIndent();
1731
1732                    pw.println("mainSessionToken: " + userState.mainSessionToken);
1733                    pw.decreaseIndent();
1734                }
1735            }
1736        }
1737    }
1738
1739    private static final class TvInputState {
1740        // A TvInputInfo object which represents the TV input.
1741        private TvInputInfo mInfo;
1742
1743        // The state of TV input. Connected by default.
1744        private int mState = INPUT_STATE_CONNECTED;
1745
1746        @Override
1747        public String toString() {
1748            return "mInfo: " + mInfo + "; mState: " + mState;
1749        }
1750    }
1751
1752    private static final class UserState {
1753        // A mapping from the TV input id to its TvInputState.
1754        private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
1755
1756        // A set of all TV input packages.
1757        private final Set<String> packageSet = new HashSet<String>();
1758
1759        // A list of all TV content rating systems defined.
1760        private final List<TvContentRatingSystemInfo>
1761                contentRatingSystemList = new ArrayList<TvContentRatingSystemInfo>();
1762
1763        // A mapping from the token of a client to its state.
1764        private final Map<IBinder, ClientState> clientStateMap =
1765                new HashMap<IBinder, ClientState>();
1766
1767        // A mapping from the name of a TV input service to its state.
1768        private final Map<ComponentName, ServiceState> serviceStateMap =
1769                new HashMap<ComponentName, ServiceState>();
1770
1771        // A mapping from the token of a TV input session to its state.
1772        private final Map<IBinder, SessionState> sessionStateMap =
1773                new HashMap<IBinder, SessionState>();
1774
1775        // A set of callbacks.
1776        private final Set<ITvInputManagerCallback> callbackSet =
1777                new HashSet<ITvInputManagerCallback>();
1778
1779        // The token of a "main" TV input session.
1780        private IBinder mainSessionToken = null;
1781
1782        // Persistent data store for all internal settings maintained by the TV input manager
1783        // service.
1784        private final PersistentDataStore persistentDataStore;
1785
1786        private UserState(Context context, int userId) {
1787            persistentDataStore = new PersistentDataStore(context, userId);
1788        }
1789    }
1790
1791    private final class ClientState implements IBinder.DeathRecipient {
1792        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
1793
1794        private IBinder mClientToken;
1795        private final int mUserId;
1796
1797        ClientState(IBinder clientToken, int userId) {
1798            mClientToken = clientToken;
1799            mUserId = userId;
1800        }
1801
1802        public boolean isEmpty() {
1803            return mSessionTokens.isEmpty();
1804        }
1805
1806        @Override
1807        public void binderDied() {
1808            synchronized (mLock) {
1809                UserState userState = getUserStateLocked(mUserId);
1810                // DO NOT remove the client state of clientStateMap in this method. It will be
1811                // removed in releaseSessionLocked().
1812                ClientState clientState = userState.clientStateMap.get(mClientToken);
1813                if (clientState != null) {
1814                    while (clientState.mSessionTokens.size() > 0) {
1815                        releaseSessionLocked(
1816                                clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
1817                    }
1818                }
1819                mClientToken = null;
1820            }
1821        }
1822    }
1823
1824    private final class ServiceState {
1825        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
1826        private final ServiceConnection mConnection;
1827        private final ComponentName mComponent;
1828        private final boolean mIsHardware;
1829        private final List<TvInputInfo> mInputList = new ArrayList<TvInputInfo>();
1830
1831        private ITvInputService mService;
1832        private ServiceCallback mCallback;
1833        private boolean mBound;
1834        private boolean mReconnecting;
1835
1836        private ServiceState(ComponentName component, int userId) {
1837            mComponent = component;
1838            mConnection = new InputServiceConnection(component, userId);
1839            mIsHardware = hasHardwarePermission(mContext.getPackageManager(), mComponent);
1840        }
1841    }
1842
1843    private final class SessionState implements IBinder.DeathRecipient {
1844        private final TvInputInfo mInfo;
1845        private final ITvInputClient mClient;
1846        private final int mSeq;
1847        private final int mCallingUid;
1848        private final int mUserId;
1849        private final IBinder mSessionToken;
1850        private ITvInputSession mSession;
1851        private Uri mLogUri;
1852        // Not null if this session represents an external device connected to a hardware TV input.
1853        private IBinder mHardwareSessionToken;
1854
1855        private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
1856                int seq, int callingUid, int userId) {
1857            mSessionToken = sessionToken;
1858            mInfo = info;
1859            mClient = client;
1860            mSeq = seq;
1861            mCallingUid = callingUid;
1862            mUserId = userId;
1863        }
1864
1865        @Override
1866        public void binderDied() {
1867            synchronized (mLock) {
1868                mSession = null;
1869                if (mClient != null) {
1870                    try {
1871                        mClient.onSessionReleased(mSeq);
1872                    } catch(RemoteException e) {
1873                        Slog.e(TAG, "error in onSessionReleased", e);
1874                    }
1875                }
1876                // If there are any other sessions based on this session, they should be released.
1877                UserState userState = getUserStateLocked(mUserId);
1878                for (SessionState sessionState : userState.sessionStateMap.values()) {
1879                    if (mSessionToken == sessionState.mHardwareSessionToken) {
1880                        releaseSessionLocked(sessionState.mSessionToken, Process.SYSTEM_UID,
1881                                mUserId);
1882                        try {
1883                            sessionState.mClient.onSessionReleased(sessionState.mSeq);
1884                        } catch (RemoteException e) {
1885                            Slog.e(TAG, "error in onSessionReleased", e);
1886                        }
1887                    }
1888                }
1889                removeSessionStateLocked(mSessionToken, mUserId);
1890            }
1891        }
1892    }
1893
1894    private final class InputServiceConnection implements ServiceConnection {
1895        private final ComponentName mComponent;
1896        private final int mUserId;
1897
1898        private InputServiceConnection(ComponentName component, int userId) {
1899            mComponent = component;
1900            mUserId = userId;
1901        }
1902
1903        @Override
1904        public void onServiceConnected(ComponentName component, IBinder service) {
1905            if (DEBUG) {
1906                Slog.d(TAG, "onServiceConnected(component=" + component + ")");
1907            }
1908            synchronized (mLock) {
1909                UserState userState = getUserStateLocked(mUserId);
1910                ServiceState serviceState = userState.serviceStateMap.get(mComponent);
1911                serviceState.mService = ITvInputService.Stub.asInterface(service);
1912
1913                // Register a callback, if we need to.
1914                if (serviceState.mIsHardware && serviceState.mCallback == null) {
1915                    serviceState.mCallback = new ServiceCallback(mComponent, mUserId);
1916                    try {
1917                        serviceState.mService.registerCallback(serviceState.mCallback);
1918                    } catch (RemoteException e) {
1919                        Slog.e(TAG, "error in registerCallback", e);
1920                    }
1921                }
1922
1923                // And create sessions, if any.
1924                for (IBinder sessionToken : serviceState.mSessionTokens) {
1925                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
1926                }
1927
1928                for (TvInputState inputState : userState.inputMap.values()) {
1929                    if (inputState.mInfo.getComponent().equals(component)
1930                            && inputState.mState != INPUT_STATE_DISCONNECTED) {
1931                        notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
1932                                inputState.mState, null);
1933                    }
1934                }
1935
1936                if (serviceState.mIsHardware) {
1937                    List<TvInputHardwareInfo> hardwareInfoList =
1938                            mTvInputHardwareManager.getHardwareList();
1939                    for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
1940                        try {
1941                            serviceState.mService.notifyHardwareAdded(hardwareInfo);
1942                        } catch (RemoteException e) {
1943                            Slog.e(TAG, "error in notifyHardwareAdded", e);
1944                        }
1945                    }
1946
1947                    List<HdmiDeviceInfo> deviceInfoList =
1948                            mTvInputHardwareManager.getHdmiDeviceList();
1949                    for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
1950                        try {
1951                            serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
1952                        } catch (RemoteException e) {
1953                            Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
1954                        }
1955                    }
1956                }
1957            }
1958        }
1959
1960        @Override
1961        public void onServiceDisconnected(ComponentName component) {
1962            if (DEBUG) {
1963                Slog.d(TAG, "onServiceDisconnected(component=" + component + ")");
1964            }
1965            if (!mComponent.equals(component)) {
1966                throw new IllegalArgumentException("Mismatched ComponentName: "
1967                        + mComponent + " (expected), " + component + " (actual).");
1968            }
1969            synchronized (mLock) {
1970                UserState userState = getUserStateLocked(mUserId);
1971                ServiceState serviceState = userState.serviceStateMap.get(mComponent);
1972                if (serviceState != null) {
1973                    serviceState.mReconnecting = true;
1974                    serviceState.mBound = false;
1975                    serviceState.mService = null;
1976                    serviceState.mCallback = null;
1977
1978                    abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
1979
1980                    for (TvInputState inputState : userState.inputMap.values()) {
1981                        if (inputState.mInfo.getComponent().equals(component)) {
1982                            notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
1983                                    INPUT_STATE_DISCONNECTED, null);
1984                        }
1985                    }
1986                }
1987            }
1988        }
1989    }
1990
1991    private final class ServiceCallback extends ITvInputServiceCallback.Stub {
1992        private final ComponentName mComponent;
1993        private final int mUserId;
1994
1995        ServiceCallback(ComponentName component, int userId) {
1996            mComponent = component;
1997            mUserId = userId;
1998        }
1999
2000        private void ensureHardwarePermission() {
2001            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2002                    != PackageManager.PERMISSION_GRANTED) {
2003                throw new SecurityException("The caller does not have hardware permission");
2004            }
2005        }
2006
2007        private void ensureValidInput(TvInputInfo inputInfo) {
2008            if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) {
2009                throw new IllegalArgumentException("Invalid TvInputInfo");
2010            }
2011        }
2012
2013        private void addTvInputLocked(TvInputInfo inputInfo) {
2014            ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
2015            serviceState.mInputList.add(inputInfo);
2016            buildTvInputListLocked(mUserId);
2017        }
2018
2019        @Override
2020        public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
2021            ensureHardwarePermission();
2022            ensureValidInput(inputInfo);
2023            synchronized (mLock) {
2024                mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo);
2025                addTvInputLocked(inputInfo);
2026            }
2027        }
2028
2029        @Override
2030        public void addHdmiTvInput(int id, TvInputInfo inputInfo) {
2031            ensureHardwarePermission();
2032            ensureValidInput(inputInfo);
2033            synchronized (mLock) {
2034                mTvInputHardwareManager.addHdmiTvInput(id, inputInfo);
2035                addTvInputLocked(inputInfo);
2036            }
2037        }
2038
2039        @Override
2040        public void removeTvInput(String inputId) {
2041            ensureHardwarePermission();
2042            synchronized (mLock) {
2043                ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
2044                boolean removed = false;
2045                for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator();
2046                        it.hasNext(); ) {
2047                    if (it.next().getId().equals(inputId)) {
2048                        it.remove();
2049                        removed = true;
2050                        break;
2051                    }
2052                }
2053                if (removed) {
2054                    buildTvInputListLocked(mUserId);
2055                    mTvInputHardwareManager.removeTvInput(inputId);
2056                } else {
2057                    Slog.e(TAG, "failed to remove input " + inputId);
2058                }
2059            }
2060        }
2061    }
2062
2063    private final class WatchLogHandler extends Handler {
2064        // There are only two kinds of watch events that can happen on the system:
2065        // 1. The current TV input session is tuned to a new channel.
2066        // 2. The session is released for some reason.
2067        // The former indicates the end of the previous log entry, if any, followed by the start of
2068        // a new entry. The latter indicates the end of the most recent entry for the given session.
2069        // Here the system supplies the database the smallest set of information only that is
2070        // sufficient to consolidate the log entries while minimizing database operations in the
2071        // system service.
2072        private static final int MSG_LOG_WATCH_START = 1;
2073        private static final int MSG_LOG_WATCH_END = 2;
2074
2075        public WatchLogHandler(Looper looper) {
2076            super(looper);
2077        }
2078
2079        @Override
2080        public void handleMessage(Message msg) {
2081            switch (msg.what) {
2082                case MSG_LOG_WATCH_START: {
2083                    SomeArgs args = (SomeArgs) msg.obj;
2084                    String packageName = (String) args.arg1;
2085                    long watchStartTime = (long) args.arg2;
2086                    long channelId = (long) args.arg3;
2087                    Bundle tuneParams = (Bundle) args.arg4;
2088                    IBinder sessionToken = (IBinder) args.arg5;
2089
2090                    ContentValues values = new ContentValues();
2091                    values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
2092                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
2093                            watchStartTime);
2094                    values.put(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID, channelId);
2095                    if (tuneParams != null) {
2096                        values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS,
2097                                encodeTuneParams(tuneParams));
2098                    }
2099                    values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
2100                            sessionToken.toString());
2101
2102                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
2103                    args.recycle();
2104                    return;
2105                }
2106                case MSG_LOG_WATCH_END: {
2107                    SomeArgs args = (SomeArgs) msg.obj;
2108                    IBinder sessionToken = (IBinder) args.arg1;
2109                    long watchEndTime = (long) args.arg2;
2110
2111                    ContentValues values = new ContentValues();
2112                    values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
2113                            watchEndTime);
2114                    values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
2115                            sessionToken.toString());
2116
2117                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
2118                    args.recycle();
2119                    return;
2120                }
2121                default: {
2122                    Slog.w(TAG, "Unhandled message code: " + msg.what);
2123                    return;
2124                }
2125            }
2126        }
2127
2128        private String encodeTuneParams(Bundle tuneParams) {
2129            StringBuilder builder = new StringBuilder();
2130            Set<String> keySet = tuneParams.keySet();
2131            Iterator<String> it = keySet.iterator();
2132            while (it.hasNext()) {
2133                String key = it.next();
2134                Object value = tuneParams.get(key);
2135                if (value == null) {
2136                    continue;
2137                }
2138                builder.append(replaceEscapeCharacters(key));
2139                builder.append("=");
2140                builder.append(replaceEscapeCharacters(value.toString()));
2141                if (it.hasNext()) {
2142                    builder.append(", ");
2143                }
2144            }
2145            return builder.toString();
2146        }
2147
2148        private String replaceEscapeCharacters(String src) {
2149            final char ESCAPE_CHARACTER = '%';
2150            final String ENCODING_TARGET_CHARACTERS = "%=,";
2151            StringBuilder builder = new StringBuilder();
2152            for (char ch : src.toCharArray()) {
2153                if (ENCODING_TARGET_CHARACTERS.indexOf(ch) >= 0) {
2154                    builder.append(ESCAPE_CHARACTER);
2155                }
2156                builder.append(ch);
2157            }
2158            return builder.toString();
2159        }
2160    }
2161
2162    final class HardwareListener implements TvInputHardwareManager.Listener {
2163        @Override
2164        public void onStateChanged(String inputId, int state) {
2165            synchronized (mLock) {
2166                setStateLocked(inputId, state, mCurrentUserId);
2167            }
2168        }
2169
2170        @Override
2171        public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
2172            synchronized (mLock) {
2173                UserState userState = getUserStateLocked(mCurrentUserId);
2174                // Broadcast the event to all hardware inputs.
2175                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2176                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
2177                    try {
2178                        serviceState.mService.notifyHardwareAdded(info);
2179                    } catch (RemoteException e) {
2180                        Slog.e(TAG, "error in notifyHardwareAdded", e);
2181                    }
2182                }
2183            }
2184        }
2185
2186        @Override
2187        public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
2188            synchronized (mLock) {
2189                UserState userState = getUserStateLocked(mCurrentUserId);
2190                // Broadcast the event to all hardware inputs.
2191                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2192                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
2193                    try {
2194                        serviceState.mService.notifyHardwareRemoved(info);
2195                    } catch (RemoteException e) {
2196                        Slog.e(TAG, "error in notifyHardwareRemoved", e);
2197                    }
2198                }
2199            }
2200        }
2201
2202        @Override
2203        public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
2204            synchronized (mLock) {
2205                UserState userState = getUserStateLocked(mCurrentUserId);
2206                // Broadcast the event to all hardware inputs.
2207                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2208                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
2209                    try {
2210                        serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
2211                    } catch (RemoteException e) {
2212                        Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
2213                    }
2214                }
2215            }
2216        }
2217
2218        @Override
2219        public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
2220            synchronized (mLock) {
2221                UserState userState = getUserStateLocked(mCurrentUserId);
2222                // Broadcast the event to all hardware inputs.
2223                for (ServiceState serviceState : userState.serviceStateMap.values()) {
2224                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
2225                    try {
2226                        serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo);
2227                    } catch (RemoteException e) {
2228                        Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
2229                    }
2230                }
2231            }
2232        }
2233
2234        @Override
2235        public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
2236            synchronized (mLock) {
2237                Integer state = null;
2238                switch (deviceInfo.getDevicePowerStatus()) {
2239                    case HdmiControlManager.POWER_STATUS_ON:
2240                        state = INPUT_STATE_CONNECTED;
2241                        break;
2242                    case HdmiControlManager.POWER_STATUS_STANDBY:
2243                    case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
2244                    case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
2245                        state = INPUT_STATE_CONNECTED_STANDBY;
2246                        break;
2247                    case HdmiControlManager.POWER_STATUS_UNKNOWN:
2248                    default:
2249                        state = null;
2250                        break;
2251                }
2252                if (state != null) {
2253                    setStateLocked(inputId, state.intValue(), mCurrentUserId);
2254                }
2255            }
2256        }
2257    }
2258}
2259