Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a26f00c authored by Sungsoo Lim's avatar Sungsoo Lim Committed by Android (Google) Code Review
Browse files

Merge "Add ClientState for handling the crash of clients." into lmp-preview-dev

parents ad3c4a13 72ad7bf9
Loading
Loading
Loading
Loading
+136 −43
Original line number Diff line number Diff line
@@ -213,11 +213,13 @@ public final class TvInputManagerService extends SystemService {
                        Slog.e(TAG, "error in unregisterCallback", e);
                    }
                }
                serviceState.mClients.clear();
                serviceState.mClientTokens.clear();
                mContext.unbindService(serviceState.mConnection);
            }
            userState.serviceStateMap.clear();

            userState.clientStateMap.clear();

            mUserStates.remove(userId);
        }
    }
@@ -282,7 +284,7 @@ public final class TvInputManagerService extends SystemService {
            }
            serviceState.mReconnecting = false;
        }
        boolean isStateEmpty = serviceState.mClients.isEmpty()
        boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
                && serviceState.mSessionTokens.isEmpty();
        if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
            // This means that the service is not yet connected but its state indicates that we
@@ -313,10 +315,22 @@ public final class TvInputManagerService extends SystemService {
        }
    }

    private ClientState createClientStateLocked(IBinder clientToken, int userId) {
        UserState userState = getUserStateLocked(userId);
        ClientState clientState = new ClientState(clientToken, userId);
        try {
            clientToken.linkToDeath(clientState, 0);
        } catch (RemoteException e) {
            Slog.e(TAG, "Client is already died.");
        }
        userState.clientStateMap.put(clientToken, clientState);
        return clientState;
    }

    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
            final int userId) {
        final SessionState sessionState =
                getUserStateLocked(userId).sessionStateMap.get(sessionToken);
        final UserState userState = getUserStateLocked(userId);
        final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
        if (DEBUG) {
            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
        }
@@ -342,6 +356,14 @@ public final class TvInputManagerService extends SystemService {
                        } catch (RemoteException e) {
                            Slog.e(TAG, "Session is already died.");
                        }

                        IBinder clientToken = sessionState.mClient.asBinder();
                        ClientState clientState = userState.clientStateMap.get(clientToken);
                        if (clientState == null) {
                            clientState = createClientStateLocked(clientToken, userId);
                        }
                        clientState.mSessionTokens.add(sessionState.mSessionToken);

                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
                                sessionToken, channels[0], sessionState.mSeq, userId);
                    }
@@ -432,7 +454,16 @@ public final class TvInputManagerService extends SystemService {
            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
        }

        // Also remove the session token from the session token list of the current service.
        // Also remove the session token from the session token list of the current client and
        // service.
        ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
        if (clientState != null) {
            clientState.mSessionTokens.remove(sessionToken);
            if (clientState.isEmpty()) {
                userState.clientStateMap.remove(sessionState.mClient.asBinder());
            }
        }

        ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
        if (serviceState != null) {
            serviceState.mSessionTokens.remove(sessionToken);
@@ -440,11 +471,45 @@ public final class TvInputManagerService extends SystemService {
        updateServiceConnectionLocked(sessionState.mInputId, userId);
    }

    private void unregisterCallbackInternalLocked(IBinder clientToken, String inputId,
            int userId) {
        UserState userState = getUserStateLocked(userId);
        ClientState clientState = userState.clientStateMap.get(clientToken);
        if (clientState != null) {
            clientState.mInputIds.remove(inputId);
            if (clientState.isEmpty()) {
                userState.clientStateMap.remove(clientToken);
            }
        }

        ServiceState serviceState = userState.serviceStateMap.get(inputId);
        if (serviceState == null) {
            return;
        }

        // Remove this client from the client list and unregister the callback.
        serviceState.mClientTokens.remove(clientToken);
        if (!serviceState.mClientTokens.isEmpty()) {
            // We have other clients who want to keep the callback. Do this later.
            return;
        }
        if (serviceState.mService == null || serviceState.mCallback == null) {
            return;
        }
        try {
            serviceState.mService.unregisterCallback(serviceState.mCallback);
        } catch (RemoteException e) {
            Slog.e(TAG, "error in unregisterCallback", e);
        } finally {
            serviceState.mCallback = null;
            updateServiceConnectionLocked(inputId, userId);
        }
    }

    private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
        for (IBinder iBinder : serviceState.mClients) {
            ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
        for (IBinder clientToken : serviceState.mClientTokens) {
            try {
                client.onAvailabilityChanged(
                ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged(
                        serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
            } catch (RemoteException e) {
                Slog.e(TAG, "error in onAvailabilityChanged", e);
@@ -507,10 +572,19 @@ public final class TvInputManagerService extends SystemService {
                                userState.inputMap.get(inputId), resolvedUserId);
                        userState.serviceStateMap.put(inputId, serviceState);
                    }
                    IBinder iBinder = client.asBinder();
                    if (!serviceState.mClients.contains(iBinder)) {
                        serviceState.mClients.add(iBinder);
                    IBinder clientToken = client.asBinder();
                    if (!serviceState.mClientTokens.contains(clientToken)) {
                        serviceState.mClientTokens.add(clientToken);
                    }

                    ClientState clientState = userState.clientStateMap.get(clientToken);
                    if (clientState == null) {
                        clientState = createClientStateLocked(clientToken, resolvedUserId);
                    }
                    if (!clientState.mInputIds.contains(inputId)) {
                        clientState.mInputIds.add(inputId);
                    }

                    if (serviceState.mService != null) {
                        if (serviceState.mCallback != null) {
                            // We already handled.
@@ -538,29 +612,7 @@ public final class TvInputManagerService extends SystemService {
            final long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    UserState userState = getUserStateLocked(resolvedUserId);
                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
                    if (serviceState == null) {
                        return;
                    }

                    // Remove this client from the client list and unregister the callback.
                    serviceState.mClients.remove(client.asBinder());
                    if (!serviceState.mClients.isEmpty()) {
                        // We have other clients who want to keep the callback. Do this later.
                        return;
                    }
                    if (serviceState.mService == null || serviceState.mCallback == null) {
                        return;
                    }
                    try {
                        serviceState.mService.unregisterCallback(serviceState.mCallback);
                    } catch (RemoteException e) {
                        Slog.e(TAG, "error in unregisterCallback", e);
                    } finally {
                        serviceState.mCallback = null;
                        updateServiceConnectionLocked(inputId, resolvedUserId);
                    }
                    unregisterCallbackInternalLocked(client.asBinder(), inputId, resolvedUserId);
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
@@ -591,8 +643,8 @@ public final class TvInputManagerService extends SystemService {

                    // Create a new session token and a session state.
                    IBinder sessionToken = new Binder();
                    SessionState sessionState = new SessionState(
                            sessionToken, inputId, client, seq, callingUid, resolvedUserId);
                    SessionState sessionState = new SessionState(sessionToken, inputId, client,
                            seq, callingUid, resolvedUserId);

                    // Add them to the global session state map of the current user.
                    userState.sessionStateMap.put(sessionToken, sessionState);
@@ -844,6 +896,10 @@ public final class TvInputManagerService extends SystemService {
        // A mapping from the TV input id to its TvInputInfo.
        private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();

        // A mapping from the token of a client to its state.
        private final Map<IBinder, ClientState> clientStateMap =
                new HashMap<IBinder, ClientState>();

        // A mapping from the name of a TV input service to its state.
        private final Map<String, ServiceState> serviceStateMap =
                new HashMap<String, ServiceState>();
@@ -853,9 +909,46 @@ public final class TvInputManagerService extends SystemService {
                new HashMap<IBinder, SessionState>();
    }

    private final class ClientState implements IBinder.DeathRecipient {
        private final List<String> mInputIds = new ArrayList<String>();
        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();

        private IBinder mClientToken;
        private final int mUserId;

        ClientState(IBinder clientToken, int userId) {
            mClientToken = clientToken;
            mUserId = userId;
        }

        public boolean isEmpty() {
            return mInputIds.isEmpty() && mSessionTokens.isEmpty();
        }

        @Override
        public void binderDied() {
            synchronized (mLock) {
                UserState userState = getUserStateLocked(mUserId);
                // DO NOT remove the client state of clientStateMap in this method. It will be
                // removed in releaseSessionLocked() or unregisterCallbackInternalLocked().
                ClientState clientState = userState.clientStateMap.get(mClientToken);
                if (clientState != null) {
                    while (clientState.mSessionTokens.size() > 0) {
                        releaseSessionLocked(
                                clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
                    }
                    while (clientState.mInputIds.size() > 0) {
                        unregisterCallbackInternalLocked(
                                mClientToken, clientState.mInputIds.get(0), mUserId);
                    }
                }
                mClientToken = null;
            }
        }
    }

    private final class ServiceState {
        // TODO: need to implement DeathRecipient for clients.
        private final List<IBinder> mClients = new ArrayList<IBinder>();
        private final List<IBinder> mClientTokens = new ArrayList<IBinder>();
        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
        private final ServiceConnection mConnection;
        private final TvInputInfo mTvInputInfo;
@@ -878,13 +971,13 @@ public final class TvInputManagerService extends SystemService {
        private final int mSeq;
        private final int mCallingUid;
        private final int mUserId;
        private final IBinder mToken;
        private final IBinder mSessionToken;
        private ITvInputSession mSession;
        private Uri mLogUri;

        private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
        private SessionState(IBinder sessionToken, String inputId, ITvInputClient client, int seq,
                int callingUid, int userId) {
            mToken = token;
            mSessionToken = sessionToken;
            mInputId = inputId;
            mClient = client;
            mSeq = seq;
@@ -903,7 +996,7 @@ public final class TvInputManagerService extends SystemService {
                        Slog.e(TAG, "error in onSessionReleased", e);
                    }
                }
                removeSessionStateLocked(mToken, mUserId);
                removeSessionStateLocked(mSessionToken, mUserId);
            }
        }
    }
@@ -927,7 +1020,7 @@ public final class TvInputManagerService extends SystemService {
                serviceState.mService = ITvInputService.Stub.asInterface(service);

                // Register a callback, if we need to.
                if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) {
                if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) {
                    serviceState.mCallback = new ServiceCallback(mUserId);
                    try {
                        serviceState.mService.registerCallback(serviceState.mCallback);