Merge "TIF: Keep recording sessions while changing user" into nyc-dev
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 7bf0cb2..52baa60 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -415,7 +415,38 @@
if (mCurrentUserId == userId) {
return;
}
- clearSessionAndServiceStatesLocked(mUserStates.get(mCurrentUserId));
+ UserState userState = mUserStates.get(mCurrentUserId);
+ List<SessionState> sessionStatesToRelease = new ArrayList<>();
+ for (SessionState sessionState : userState.sessionStateMap.values()) {
+ if (sessionState.session != null && !sessionState.isRecordingSession) {
+ sessionStatesToRelease.add(sessionState);
+ }
+ }
+ for (SessionState sessionState : sessionStatesToRelease) {
+ try {
+ sessionState.session.release();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in release", e);
+ }
+ clearSessionAndNotifyClientLocked(sessionState);
+ }
+
+ for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator();
+ it.hasNext(); ) {
+ ComponentName component = it.next();
+ ServiceState serviceState = userState.serviceStateMap.get(component);
+ if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
+ if (serviceState.callback != null) {
+ try {
+ serviceState.service.unregisterCallback(serviceState.callback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
+ }
+ mContext.unbindService(serviceState.connection);
+ it.remove();
+ }
+ }
mCurrentUserId = userId;
getOrCreateUserStateLocked(userId);
@@ -426,13 +457,61 @@
}
}
+ private void clearSessionAndNotifyClientLocked(SessionState state) {
+ if (state.client != null) {
+ try {
+ state.client.onSessionReleased(state.seq);
+ } catch(RemoteException e) {
+ Slog.e(TAG, "error in onSessionReleased", e);
+ }
+ }
+ // If there are any other sessions based on this session, they should be released.
+ UserState userState = getOrCreateUserStateLocked(state.userId);
+ for (SessionState sessionState : userState.sessionStateMap.values()) {
+ if (state.sessionToken == sessionState.hardwareSessionToken) {
+ releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID, state.userId);
+ try {
+ sessionState.client.onSessionReleased(sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSessionReleased", e);
+ }
+ }
+ }
+ removeSessionStateLocked(state.sessionToken, state.userId);
+ }
+
private void removeUser(int userId) {
synchronized (mLock) {
UserState userState = mUserStates.get(userId);
if (userState == null) {
return;
}
- clearSessionAndServiceStatesLocked(userState);
+ // Release all created sessions.
+ for (SessionState state : userState.sessionStateMap.values()) {
+ if (state.session != null) {
+ try {
+ state.session.release();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in release", e);
+ }
+ }
+ }
+ userState.sessionStateMap.clear();
+
+ // Unregister all callbacks and unbind all services.
+ for (ServiceState serviceState : userState.serviceStateMap.values()) {
+ if (serviceState.service != null) {
+ if (serviceState.callback != null) {
+ try {
+ serviceState.service.unregisterCallback(serviceState.callback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
+ }
+ mContext.unbindService(serviceState.connection);
+ }
+ }
+ userState.serviceStateMap.clear();
// Clear everything else.
userState.inputMap.clear();
@@ -446,35 +525,6 @@
}
}
- private void clearSessionAndServiceStatesLocked(UserState userState) {
- // Release created sessions.
- for (SessionState state : userState.sessionStateMap.values()) {
- if (state.session != null) {
- try {
- state.session.release();
- } catch (RemoteException e) {
- Slog.e(TAG, "error in release", e);
- }
- }
- }
- userState.sessionStateMap.clear();
-
- // Unregister all callbacks and unbind all services.
- for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (serviceState.service != null) {
- if (serviceState.callback != null) {
- try {
- serviceState.service.unregisterCallback(serviceState.callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in unregisterCallback", e);
- }
- }
- mContext.unbindService(serviceState.connection);
- }
- }
- userState.serviceStateMap.clear();
- }
-
private ContentResolver getContentResolverForUser(int userId) {
UserHandle user = new UserHandle(userId);
Context context;
@@ -539,11 +589,6 @@
false, methodName, null);
}
- private static boolean shouldMaintainConnection(ServiceState serviceState) {
- return !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
- // TODO: Find a way to maintain connection to hardware TV input service only when necessary.
- }
-
private void updateServiceConnectionLocked(ComponentName component, int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
ServiceState serviceState = userState.serviceStateMap.get(component);
@@ -557,8 +602,19 @@
}
serviceState.reconnecting = false;
}
- boolean maintainConnection = shouldMaintainConnection(serviceState);
- if (serviceState.service == null && maintainConnection && userId == mCurrentUserId) {
+
+ boolean shouldBind;
+ if (userId == mCurrentUserId) {
+ shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
+ } else {
+ // For a non-current user,
+ // if sessionTokens is not empty, it contains recording sessions only
+ // because other sessions must have been removed while switching user
+ // and non-recording sessions are not created by createSession().
+ shouldBind = !serviceState.sessionTokens.isEmpty();
+ }
+
+ if (serviceState.service == null && shouldBind) {
// This means that the service is not yet connected but its state indicates that we
// have pending requests. Then, connect the service.
if (serviceState.bound) {
@@ -575,7 +631,7 @@
i, serviceState.connection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
new UserHandle(userId));
- } else if (serviceState.service != null && !maintainConnection) {
+ } else if (serviceState.service != null && !shouldBind) {
// This means that the service is already connected but its state indicates that we have
// nothing to do with it. Then, disconnect the service.
if (DEBUG) {
@@ -811,7 +867,7 @@
int oldState = inputState.state;
inputState.state = state;
if (serviceState != null && serviceState.service == null
- && shouldMaintainConnection(serviceState)) {
+ && (!serviceState.sessionTokens.isEmpty() || serviceState.isHardware)) {
// We don't notify state change while reconnecting. It should remain disconnected.
return;
}
@@ -1080,6 +1136,13 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
+ if (userId != mCurrentUserId && !isRecordingSession) {
+ // A non-recording session of a backgroud (non-current) user
+ // should not be created.
+ // Let the client get onConnectionFailed callback for this case.
+ sendSessionTokenToClientLocked(client, inputId, null, null, seq);
+ return;
+ }
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
TvInputState inputState = userState.inputMap.get(inputId);
if (inputState == null) {
@@ -2073,27 +2136,7 @@
public void binderDied() {
synchronized (mLock) {
session = null;
- if (client != null) {
- try {
- client.onSessionReleased(seq);
- } catch(RemoteException e) {
- Slog.e(TAG, "error in onSessionReleased", e);
- }
- }
- // If there are any other sessions based on this session, they should be released.
- UserState userState = getOrCreateUserStateLocked(userId);
- for (SessionState sessionState : userState.sessionStateMap.values()) {
- if (sessionToken == sessionState.hardwareSessionToken) {
- releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID,
- userId);
- try {
- sessionState.client.onSessionReleased(sessionState.seq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onSessionReleased", e);
- }
- }
- }
- removeSessionStateLocked(sessionToken, userId);
+ clearSessionAndNotifyClientLocked(this);
}
}
}