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