Merge "Add ClientState for handling the crash of clients." into lmp-preview-dev
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user