Merge "fix app prediction service doesn't clean-up properly." into rvc-qpr-dev am: 4cb7d8c878

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/12221409

Change-Id: I880aa857e53bc499837b8cb2c1eeff84137a55be
This commit is contained in:
TreeHugger Robot
2020-08-24 18:57:50 +00:00
committed by Automerger Merge Worker
4 changed files with 92 additions and 56 deletions

View File

@@ -83,6 +83,8 @@ public final class AppPredictor {
private final AppPredictionSessionId mSessionId; private final AppPredictionSessionId mSessionId;
private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>(); private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
private final IBinder mToken = new Binder();
/** /**
* Creates a new Prediction client. * Creates a new Prediction client.
* <p> * <p>
@@ -98,7 +100,7 @@ public final class AppPredictor {
mSessionId = new AppPredictionSessionId( mSessionId = new AppPredictionSessionId(
context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId()); context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
try { try {
mPredictionManager.createPredictionSession(predictionContext, mSessionId); mPredictionManager.createPredictionSession(predictionContext, mSessionId, mToken);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Failed to create predictor", e); Log.e(TAG, "Failed to create predictor", e);
e.rethrowAsRuntimeException(); e.rethrowAsRuntimeException();

View File

@@ -29,7 +29,7 @@ import android.content.pm.ParceledListSlice;
interface IPredictionManager { interface IPredictionManager {
void createPredictionSession(in AppPredictionContext context, void createPredictionSession(in AppPredictionContext context,
in AppPredictionSessionId sessionId); in AppPredictionSessionId sessionId, in IBinder token);
void notifyAppTargetEvent(in AppPredictionSessionId sessionId, in AppTargetEvent event); void notifyAppTargetEvent(in AppPredictionSessionId sessionId, in AppTargetEvent event);

View File

@@ -34,6 +34,7 @@ import android.app.prediction.IPredictionManager;
import android.content.Context; import android.content.Context;
import android.content.pm.ParceledListSlice; import android.content.pm.ParceledListSlice;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import android.os.ShellCallback; import android.os.ShellCallback;
import android.util.Slog; import android.util.Slog;
@@ -108,9 +109,9 @@ public class AppPredictionManagerService extends
@Override @Override
public void createPredictionSession(@NonNull AppPredictionContext context, public void createPredictionSession(@NonNull AppPredictionContext context,
@NonNull AppPredictionSessionId sessionId) { @NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) {
runForUserLocked("createPredictionSession", sessionId, runForUserLocked("createPredictionSession", sessionId, (service) ->
(service) -> service.onCreatePredictionSessionLocked(context, sessionId)); service.onCreatePredictionSessionLocked(context, sessionId, token));
} }
@Override @Override

View File

@@ -30,6 +30,7 @@ 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.RemoteCallbackList; import android.os.RemoteCallbackList;
import android.os.RemoteException; import android.os.RemoteException;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
@@ -44,8 +45,6 @@ import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService; import com.android.server.infra.AbstractPerUserSystemService;
import com.android.server.people.PeopleServiceInternal; import com.android.server.people.PeopleServiceInternal;
import java.util.function.Consumer;
/** /**
* Per-user instance of {@link AppPredictionManagerService}. * Per-user instance of {@link AppPredictionManagerService}.
*/ */
@@ -112,17 +111,24 @@ public class AppPredictionPerUserService extends
*/ */
@GuardedBy("mLock") @GuardedBy("mLock")
public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context, public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
@NonNull AppPredictionSessionId sessionId) { @NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) {
if (!mSessionInfos.containsKey(sessionId)) { final boolean usesPeopleService = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false);
DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, final boolean serviceExists = resolveService(sessionId, false,
PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false), usesPeopleService, s -> s.onCreatePredictionSession(context, sessionId));
this::removeAppPredictionSessionInfo)); if (serviceExists && !mSessionInfos.containsKey(sessionId)) {
final AppPredictionSessionInfo sessionInfo = new AppPredictionSessionInfo(
sessionId, context, usesPeopleService, token, () -> {
synchronized (mLock) {
onDestroyPredictionSessionLocked(sessionId);
}
});
if (sessionInfo.linkToDeath()) {
mSessionInfos.put(sessionId, sessionInfo);
} else {
// destroy the session if calling process is already dead
onDestroyPredictionSessionLocked(sessionId);
} }
final boolean serviceExists = resolveService(sessionId, s ->
s.onCreatePredictionSession(context, sessionId), true);
if (!serviceExists) {
mSessionInfos.remove(sessionId);
} }
} }
@@ -132,7 +138,10 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock") @GuardedBy("mLock")
public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId, public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull AppTargetEvent event) { @NonNull AppTargetEvent event) {
resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event), false); final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (sessionInfo == null) return;
resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
s -> s.notifyAppTargetEvent(sessionId, event));
} }
/** /**
@@ -141,8 +150,10 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock") @GuardedBy("mLock")
public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId, public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull String launchLocation, @NonNull ParceledListSlice targetIds) { @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
resolveService(sessionId, s -> final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds), false); if (sessionInfo == null) return;
resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
s -> s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds));
} }
/** /**
@@ -151,7 +162,10 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock") @GuardedBy("mLock")
public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId, public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) { @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback), true); final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (sessionInfo == null) return;
resolveService(sessionId, true, sessionInfo.mUsesPeopleService,
s -> s.sortAppTargets(sessionId, targets, callback));
} }
/** /**
@@ -160,10 +174,12 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock") @GuardedBy("mLock")
public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId, public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) { @NonNull IPredictionCallback callback) {
final boolean serviceExists = resolveService(sessionId, s ->
s.registerPredictionUpdates(sessionId, callback), false);
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (serviceExists && sessionInfo != null) { if (sessionInfo == null) return;
final boolean serviceExists = resolveService(sessionId, false,
sessionInfo.mUsesPeopleService,
s -> s.registerPredictionUpdates(sessionId, callback));
if (serviceExists) {
sessionInfo.addCallbackLocked(callback); sessionInfo.addCallbackLocked(callback);
} }
} }
@@ -174,10 +190,12 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock") @GuardedBy("mLock")
public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId, public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) { @NonNull IPredictionCallback callback) {
final boolean serviceExists = resolveService(sessionId, s ->
s.unregisterPredictionUpdates(sessionId, callback), false);
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (serviceExists && sessionInfo != null) { if (sessionInfo == null) return;
final boolean serviceExists = resolveService(sessionId, false,
sessionInfo.mUsesPeopleService,
s -> s.unregisterPredictionUpdates(sessionId, callback));
if (serviceExists) {
sessionInfo.removeCallbackLocked(callback); sessionInfo.removeCallbackLocked(callback);
} }
} }
@@ -187,7 +205,10 @@ public class AppPredictionPerUserService extends
*/ */
@GuardedBy("mLock") @GuardedBy("mLock")
public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) { public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId), true); final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (sessionInfo == null) return;
resolveService(sessionId, true, sessionInfo.mUsesPeopleService,
s -> s.requestPredictionUpdate(sessionId));
} }
/** /**
@@ -195,12 +216,14 @@ public class AppPredictionPerUserService extends
*/ */
@GuardedBy("mLock") @GuardedBy("mLock")
public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) { public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
final boolean serviceExists = resolveService(sessionId, s -> if (isDebug()) {
s.onDestroyPredictionSession(sessionId), false); Slog.d(TAG, "onDestroyPredictionSessionLocked(): sessionId=" + sessionId);
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (serviceExists && sessionInfo != null) {
sessionInfo.destroy();
} }
final AppPredictionSessionInfo sessionInfo = mSessionInfos.remove(sessionId);
if (sessionInfo == null) return;
resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
s -> s.onDestroyPredictionSession(sessionId));
sessionInfo.destroy();
} }
@Override @Override
@@ -291,27 +314,18 @@ public class AppPredictionPerUserService extends
} }
for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) { for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) {
sessionInfo.resurrectSessionLocked(this); sessionInfo.resurrectSessionLocked(this, sessionInfo.mToken);
}
}
private void removeAppPredictionSessionInfo(AppPredictionSessionId sessionId) {
if (isDebug()) {
Slog.d(TAG, "removeAppPredictionSessionInfo(): sessionId=" + sessionId);
}
synchronized (mLock) {
mSessionInfos.remove(sessionId);
} }
} }
@GuardedBy("mLock") @GuardedBy("mLock")
@Nullable @Nullable
protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId, protected boolean resolveService(
@NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb, @NonNull final AppPredictionSessionId sessionId,
boolean sendImmediately) { boolean sendImmediately,
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); boolean usesPeopleService,
if (sessionInfo == null) return false; @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) {
if (sessionInfo.mUsesPeopleService) { if (usesPeopleService) {
final IPredictionService service = final IPredictionService service =
LocalServices.getService(PeopleServiceInternal.class); LocalServices.getService(PeopleServiceInternal.class);
if (service != null) { if (service != null) {
@@ -368,7 +382,9 @@ public class AppPredictionPerUserService extends
private final AppPredictionContext mPredictionContext; private final AppPredictionContext mPredictionContext;
private final boolean mUsesPeopleService; private final boolean mUsesPeopleService;
@NonNull @NonNull
private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction; final IBinder mToken;
@NonNull
final IBinder.DeathRecipient mDeathRecipient;
private final RemoteCallbackList<IPredictionCallback> mCallbacks = private final RemoteCallbackList<IPredictionCallback> mCallbacks =
new RemoteCallbackList<IPredictionCallback>() { new RemoteCallbackList<IPredictionCallback>() {
@@ -388,14 +404,16 @@ public class AppPredictionPerUserService extends
@NonNull final AppPredictionSessionId id, @NonNull final AppPredictionSessionId id,
@NonNull final AppPredictionContext predictionContext, @NonNull final AppPredictionContext predictionContext,
final boolean usesPeopleService, final boolean usesPeopleService,
@NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) { @NonNull final IBinder token,
@NonNull final IBinder.DeathRecipient deathRecipient) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id); Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
} }
mSessionId = id; mSessionId = id;
mPredictionContext = predictionContext; mPredictionContext = predictionContext;
mUsesPeopleService = usesPeopleService; mUsesPeopleService = usesPeopleService;
mRemoveSessionInfoAction = removeSessionInfoAction; mToken = token;
mDeathRecipient = deathRecipient;
} }
void addCallbackLocked(IPredictionCallback callback) { void addCallbackLocked(IPredictionCallback callback) {
@@ -414,23 +432,38 @@ public class AppPredictionPerUserService extends
mCallbacks.unregister(callback); mCallbacks.unregister(callback);
} }
boolean linkToDeath() {
try {
mToken.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
if (DEBUG) {
Slog.w(TAG, "Caller is dead before session can be started, sessionId: "
+ mSessionId);
}
return false;
}
return true;
}
void destroy() { void destroy() {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "Removing all callbacks for session Id=" + mSessionId Slog.d(TAG, "Removing all callbacks for session Id=" + mSessionId
+ " and " + mCallbacks.getRegisteredCallbackCount() + " callbacks."); + " and " + mCallbacks.getRegisteredCallbackCount() + " callbacks.");
} }
if (mToken != null) {
mToken.unlinkToDeath(mDeathRecipient, 0);
}
mCallbacks.kill(); mCallbacks.kill();
mRemoveSessionInfoAction.accept(mSessionId);
} }
void resurrectSessionLocked(AppPredictionPerUserService service) { void resurrectSessionLocked(AppPredictionPerUserService service, IBinder token) {
int callbackCount = mCallbacks.getRegisteredCallbackCount(); int callbackCount = mCallbacks.getRegisteredCallbackCount();
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked() Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
+ ") for session Id=" + mSessionId + " and " + ") for session Id=" + mSessionId + " and "
+ callbackCount + " callbacks."); + callbackCount + " callbacks.");
} }
service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId); service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId, token);
mCallbacks.broadcast( mCallbacks.broadcast(
callback -> service.registerPredictionUpdatesLocked(mSessionId, callback)); callback -> service.registerPredictionUpdatesLocked(mSessionId, callback));
} }