Merge "Restores state of AppPredictionService after remote service crash" into qt-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b143fe4928
@@ -28,13 +28,17 @@ import android.content.pm.PackageManager;
|
|||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.pm.ParceledListSlice;
|
import android.content.pm.ParceledListSlice;
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.service.appprediction.AppPredictionService;
|
import android.service.appprediction.AppPredictionService;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.server.infra.AbstractPerUserSystemService;
|
import com.android.server.infra.AbstractPerUserSystemService;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per-user instance of {@link AppPredictionManagerService}.
|
* Per-user instance of {@link AppPredictionManagerService}.
|
||||||
*/
|
*/
|
||||||
@@ -48,6 +52,17 @@ public class AppPredictionPerUserService extends
|
|||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private RemoteAppPredictionService mRemoteService;
|
private RemoteAppPredictionService mRemoteService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When {@code true}, remote service died but service state is kept so it's restored after
|
||||||
|
* the system re-binds to it.
|
||||||
|
*/
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private boolean mZombie;
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private final ArrayMap<AppPredictionSessionId, AppPredictionSessionInfo> mSessionInfos =
|
||||||
|
new ArrayMap<>();
|
||||||
|
|
||||||
protected AppPredictionPerUserService(AppPredictionManagerService master,
|
protected AppPredictionPerUserService(AppPredictionManagerService master,
|
||||||
Object lock, int userId) {
|
Object lock, int userId) {
|
||||||
super(master, lock, userId);
|
super(master, lock, userId);
|
||||||
@@ -92,6 +107,16 @@ public class AppPredictionPerUserService extends
|
|||||||
final RemoteAppPredictionService service = getRemoteServiceLocked();
|
final RemoteAppPredictionService service = getRemoteServiceLocked();
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
service.onCreatePredictionSession(context, sessionId);
|
service.onCreatePredictionSession(context, sessionId);
|
||||||
|
|
||||||
|
mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, () -> {
|
||||||
|
synchronized (mLock) {
|
||||||
|
AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
|
||||||
|
if (sessionInfo != null) {
|
||||||
|
sessionInfo.removeAllCallbacksLocked();
|
||||||
|
mSessionInfos.remove(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +165,11 @@ public class AppPredictionPerUserService extends
|
|||||||
final RemoteAppPredictionService service = getRemoteServiceLocked();
|
final RemoteAppPredictionService service = getRemoteServiceLocked();
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
service.registerPredictionUpdates(sessionId, callback);
|
service.registerPredictionUpdates(sessionId, callback);
|
||||||
|
|
||||||
|
AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
|
||||||
|
if (sessionInfo != null) {
|
||||||
|
sessionInfo.addCallbackLocked(callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +182,11 @@ public class AppPredictionPerUserService extends
|
|||||||
final RemoteAppPredictionService service = getRemoteServiceLocked();
|
final RemoteAppPredictionService service = getRemoteServiceLocked();
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
service.unregisterPredictionUpdates(sessionId, callback);
|
service.unregisterPredictionUpdates(sessionId, callback);
|
||||||
|
|
||||||
|
AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
|
||||||
|
if (sessionInfo != null) {
|
||||||
|
sessionInfo.removeCallbackLocked(callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +209,12 @@ public class AppPredictionPerUserService extends
|
|||||||
final RemoteAppPredictionService service = getRemoteServiceLocked();
|
final RemoteAppPredictionService service = getRemoteServiceLocked();
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
service.onDestroyPredictionSession(sessionId);
|
service.onDestroyPredictionSession(sessionId);
|
||||||
|
|
||||||
|
AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
|
||||||
|
if (sessionInfo != null) {
|
||||||
|
sessionInfo.removeAllCallbacksLocked();
|
||||||
|
mSessionInfos.remove(sessionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,17 +223,54 @@ public class AppPredictionPerUserService extends
|
|||||||
if (isDebug()) {
|
if (isDebug()) {
|
||||||
Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
|
Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do nothing, we are just proxying to the prediction service
|
// Do nothing, we are just proxying to the prediction service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectedStateChanged(boolean connected) {
|
||||||
|
if (isDebug()) {
|
||||||
|
Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected);
|
||||||
|
}
|
||||||
|
if (connected) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mZombie) {
|
||||||
|
// Sanity check - shouldn't happen
|
||||||
|
if (mRemoteService == null) {
|
||||||
|
Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mZombie = false;
|
||||||
|
resurrectSessionsLocked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceDied(RemoteAppPredictionService service) {
|
public void onServiceDied(RemoteAppPredictionService service) {
|
||||||
if (isDebug()) {
|
if (isDebug()) {
|
||||||
Slog.d(TAG, "onServiceDied():");
|
Slog.w(TAG, "onServiceDied(): service=" + service);
|
||||||
|
}
|
||||||
|
synchronized (mLock) {
|
||||||
|
mZombie = true;
|
||||||
|
}
|
||||||
|
// Do nothing, eventually the system will bind to the remote service again...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after the remote service connected, it's used to restore state from a 'zombie'
|
||||||
|
* service (i.e., after it died).
|
||||||
|
*/
|
||||||
|
private void resurrectSessionsLocked() {
|
||||||
|
final int numSessions = mSessionInfos.size();
|
||||||
|
if (isDebug()) {
|
||||||
|
Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
|
||||||
|
+ numSessions + " sessions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do nothing, we are just proxying to the prediction service
|
for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) {
|
||||||
|
sessionInfo.resurrectSessionLocked(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@@ -215,4 +293,57 @@ public class AppPredictionPerUserService extends
|
|||||||
|
|
||||||
return mRemoteService;
|
return mRemoteService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class AppPredictionSessionInfo {
|
||||||
|
private final AppPredictionSessionId mSessionId;
|
||||||
|
private final AppPredictionContext mContext;
|
||||||
|
private final ArrayList<IPredictionCallback> mCallbacks = new ArrayList<>();
|
||||||
|
private final IBinder.DeathRecipient mBinderDeathHandler;
|
||||||
|
|
||||||
|
AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext context,
|
||||||
|
IBinder.DeathRecipient binderDeathHandler) {
|
||||||
|
mSessionId = id;
|
||||||
|
mContext = context;
|
||||||
|
mBinderDeathHandler = binderDeathHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addCallbackLocked(IPredictionCallback callback) {
|
||||||
|
if (mBinderDeathHandler != null) {
|
||||||
|
try {
|
||||||
|
callback.asBinder().linkToDeath(mBinderDeathHandler, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.e(TAG, "Failed to link to death: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCallbacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeCallbackLocked(IPredictionCallback callback) {
|
||||||
|
if (mBinderDeathHandler != null) {
|
||||||
|
callback.asBinder().unlinkToDeath(mBinderDeathHandler, 0);
|
||||||
|
}
|
||||||
|
mCallbacks.remove(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeAllCallbacksLocked() {
|
||||||
|
if (mBinderDeathHandler != null) {
|
||||||
|
for (IPredictionCallback callback : mCallbacks) {
|
||||||
|
callback.asBinder().unlinkToDeath(mBinderDeathHandler, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCallbacks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resurrectSessionLocked(AppPredictionPerUserService service) {
|
||||||
|
if (service.isDebug()) {
|
||||||
|
Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
|
||||||
|
+ ") for session Id=" + mSessionId + " and "
|
||||||
|
+ mCallbacks.size() + " callbacks.");
|
||||||
|
}
|
||||||
|
service.onCreatePredictionSessionLocked(mContext, mSessionId);
|
||||||
|
for (IPredictionCallback callback : mCallbacks) {
|
||||||
|
service.registerPredictionUpdatesLocked(mSessionId, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ public class RemoteAppPredictionService extends
|
|||||||
|
|
||||||
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
|
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
|
||||||
|
|
||||||
|
private final RemoteAppPredictionServiceCallbacks mCallback;
|
||||||
|
|
||||||
public RemoteAppPredictionService(Context context, String serviceInterface,
|
public RemoteAppPredictionService(Context context, String serviceInterface,
|
||||||
ComponentName componentName, int userId,
|
ComponentName componentName, int userId,
|
||||||
RemoteAppPredictionServiceCallbacks callback, boolean bindInstantServiceAllowed,
|
RemoteAppPredictionServiceCallbacks callback, boolean bindInstantServiceAllowed,
|
||||||
@@ -50,6 +52,7 @@ public class RemoteAppPredictionService extends
|
|||||||
context.getMainThreadHandler(),
|
context.getMainThreadHandler(),
|
||||||
bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
|
bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
|
||||||
verbose, /* initialCapacity= */ 1);
|
verbose, /* initialCapacity= */ 1);
|
||||||
|
mCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -141,5 +144,17 @@ public class RemoteAppPredictionService extends
|
|||||||
* Notifies a the failure or timeout of a remote call.
|
* Notifies a the failure or timeout of a remote call.
|
||||||
*/
|
*/
|
||||||
void onFailureOrTimeout(boolean timedOut);
|
void onFailureOrTimeout(boolean timedOut);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies change in connected state of the remote service.
|
||||||
|
*/
|
||||||
|
void onConnectedStateChanged(boolean connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // from AbstractRemoteService
|
||||||
|
protected void handleOnConnectedStateChanged(boolean connected) {
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onConnectedStateChanged(connected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user