Merge "Restores state of AppPredictionService after remote service crash" into qt-dev

am: b143fe4928

Change-Id: I294a1c8759baaaa799a6ead5cabec493cfcd2c7a
This commit is contained in:
Mehdi Alizadeh
2019-05-30 15:41:46 -07:00
committed by android-build-merger
2 changed files with 149 additions and 3 deletions

View File

@@ -28,13 +28,17 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.appprediction.AppPredictionService;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;
import java.util.ArrayList;
/**
* Per-user instance of {@link AppPredictionManagerService}.
*/
@@ -48,6 +52,17 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
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,
Object lock, int userId) {
super(master, lock, userId);
@@ -92,6 +107,16 @@ public class AppPredictionPerUserService extends
final RemoteAppPredictionService service = getRemoteServiceLocked();
if (service != null) {
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();
if (service != null) {
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();
if (service != null) {
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();
if (service != null) {
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()) {
Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
}
// 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
public void onServiceDied(RemoteAppPredictionService service) {
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")
@@ -215,4 +293,57 @@ public class AppPredictionPerUserService extends
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);
}
}
}
}

View File

@@ -42,6 +42,8 @@ public class RemoteAppPredictionService extends
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
private final RemoteAppPredictionServiceCallbacks mCallback;
public RemoteAppPredictionService(Context context, String serviceInterface,
ComponentName componentName, int userId,
RemoteAppPredictionServiceCallbacks callback, boolean bindInstantServiceAllowed,
@@ -50,6 +52,7 @@ public class RemoteAppPredictionService extends
context.getMainThreadHandler(),
bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
verbose, /* initialCapacity= */ 1);
mCallback = callback;
}
@Override
@@ -141,5 +144,17 @@ public class RemoteAppPredictionService extends
* Notifies a the failure or timeout of a remote call.
*/
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);
}
}
}