Add ClientState for handling the crash of clients.
Bug: 15054040 Change-Id: I4dd8d1c350186fe705daa20c9944bc8e4dd556c2
This commit is contained in:
@@ -204,11 +204,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);
|
||||
}
|
||||
}
|
||||
@@ -273,7 +275,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
|
||||
@@ -304,10 +306,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 + ")");
|
||||
}
|
||||
@@ -333,6 +347,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);
|
||||
}
|
||||
@@ -423,7 +445,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);
|
||||
@@ -431,11 +462,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);
|
||||
@@ -498,10 +563,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.
|
||||
@@ -529,29 +603,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);
|
||||
@@ -582,8 +634,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);
|
||||
@@ -835,6 +887,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>();
|
||||
@@ -844,9 +900,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;
|
||||
@@ -869,13 +962,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;
|
||||
@@ -894,7 +987,7 @@ public final class TvInputManagerService extends SystemService {
|
||||
Slog.e(TAG, "error in onSessionReleased", e);
|
||||
}
|
||||
}
|
||||
removeSessionStateLocked(mToken, mUserId);
|
||||
removeSessionStateLocked(mSessionToken, mUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -918,7 +1011,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);
|
||||
|
||||
Reference in New Issue
Block a user