diff options
author | 2024-01-28 13:38:03 -0800 | |
---|---|---|
committer | 2024-02-08 22:17:43 -0800 | |
commit | bfd6be48ec1624abb963b1a19812709472041228 (patch) | |
tree | f94fcbe4ba9fd5b1327642e9ee25e270f2d8edd1 | |
parent | 39255ff49ea8c2980faea56b355db7c376dfcdb9 (diff) |
Add new method 'requestServiceFeatures' in AppPredictionService. It allows Frontend to get backend service impl features info (e.g. feature readiness)
Bug: 292565550
Test: atest AppPredictionServiceTest
Change-Id: I0f2ec9ba0c1b332fa73afb461c29fe25de9b01e2
10 files changed, 183 insertions, 0 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index f5bf437a738d..60ef8b47fc16 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -48,6 +48,7 @@ aconfig_srcjars = [ ":android.service.controls.flags-aconfig-java{.generated_srcjars}", ":android.service.dreams.flags-aconfig-java{.generated_srcjars}", ":android.service.notification.flags-aconfig-java{.generated_srcjars}", + ":android.service.appprediction.flags-aconfig-java{.generated_srcjars}", ":android.service.voice.flags-aconfig-java{.generated_srcjars}", ":android.speech.flags-aconfig-java{.generated_srcjars}", ":android.tracing.flags-aconfig-java{.generated_srcjars}", @@ -112,6 +113,7 @@ stubs_defaults { "android.provider.flags-aconfig", "android.security.flags-aconfig", "android.server.app.flags-aconfig", + "android.service.appprediction.flags-aconfig", "android.service.autofill.flags-aconfig", "android.service.chooser.flags-aconfig", "android.service.controls.flags-aconfig", @@ -698,6 +700,19 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// App prediction +aconfig_declarations { + name: "android.service.appprediction.flags-aconfig", + package: "android.service.appprediction.flags", + srcs: ["core/java/android/service/appprediction/flags/*.aconfig"], +} + +java_aconfig_library { + name: "android.service.appprediction.flags-aconfig-java", + aconfig_declarations: "android.service.appprediction.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Controls aconfig_declarations { name: "android.service.controls.flags-aconfig", diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 318badfc8caa..783180ab659c 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2193,6 +2193,7 @@ package android.app.prediction { method public void notifyLaunchLocationShown(@NonNull String, @NonNull java.util.List<android.app.prediction.AppTargetId>); method public void registerPredictionUpdates(@NonNull java.util.concurrent.Executor, @NonNull android.app.prediction.AppPredictor.Callback); method public void requestPredictionUpdate(); + method @FlaggedApi("android.service.appprediction.flags.service_features_api") public void requestServiceFeatures(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.os.Bundle>); method @Nullable public void sortTargets(@NonNull java.util.List<android.app.prediction.AppTarget>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.prediction.AppTarget>>); method public void unregisterPredictionUpdates(@NonNull android.app.prediction.AppPredictor.Callback); } @@ -11833,6 +11834,7 @@ package android.service.appprediction { method @MainThread public void onDestroyPredictionSession(@NonNull android.app.prediction.AppPredictionSessionId); method @MainThread public abstract void onLaunchLocationShown(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull String, @NonNull java.util.List<android.app.prediction.AppTargetId>); method @MainThread public abstract void onRequestPredictionUpdate(@NonNull android.app.prediction.AppPredictionSessionId); + method @FlaggedApi("android.service.appprediction.flags.service_features_api") @MainThread public void onRequestServiceFeatures(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull java.util.function.Consumer<android.os.Bundle>); method @MainThread public abstract void onSortAppTargets(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull java.util.List<android.app.prediction.AppTarget>, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<java.util.List<android.app.prediction.AppTarget>>); method @MainThread public void onStartPredictionUpdates(); method @MainThread public void onStopPredictionUpdates(); diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java index d628b7f92c6c..0c1a28a9eee1 100644 --- a/core/java/android/app/prediction/AppPredictor.java +++ b/core/java/android/app/prediction/AppPredictor.java @@ -16,6 +16,7 @@ package android.app.prediction; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -24,9 +25,12 @@ import android.app.prediction.IPredictionCallback.Stub; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; +import android.service.appprediction.flags.Flags; import android.util.ArrayMap; import android.util.Log; @@ -263,6 +267,34 @@ public final class AppPredictor { } /** + * Requests a Bundle which includes service features info or {@code null} if the service is not + * available. + * + * @param callbackExecutor The callback executor to use when calling the callback. It cannot be + * null. + * @param callback The callback to return the Bundle which includes service features info. It + * cannot be null. + * + * @throws IllegalStateException If this AppPredictor has already been destroyed. + * @throws RuntimeException If there is a failure communicating with the remote service. + */ + @FlaggedApi(Flags.FLAG_SERVICE_FEATURES_API) + public void requestServiceFeatures(@NonNull Executor callbackExecutor, + @NonNull Consumer<Bundle> callback) { + if (mIsClosed.get()) { + throw new IllegalStateException("This client has already been destroyed."); + } + + try { + mPredictionManager.requestServiceFeatures(mSessionId, + new RemoteCallbackWrapper(callbackExecutor, callback)); + } catch (RemoteException e) { + Log.e(TAG, "Failed to request service feature info", e); + e.rethrowAsRuntimeException(); + } + } + + /** * Destroys the client and unregisters the callback. Any method on this class after this call * with throw {@link IllegalStateException}. */ @@ -347,6 +379,28 @@ public final class AppPredictor { } } + static class RemoteCallbackWrapper extends IRemoteCallback.Stub { + + private final Consumer<Bundle> mCallback; + private final Executor mExecutor; + + RemoteCallbackWrapper(@NonNull Executor callbackExecutor, + @NonNull Consumer<Bundle> callback) { + mExecutor = callbackExecutor; + mCallback = callback; + } + + @Override + public void sendResult(Bundle result) { + final long identity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.accept(result)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + private static class Token { static final IBinder sBinder = new Binder(TAG); } diff --git a/core/java/android/app/prediction/IPredictionManager.aidl b/core/java/android/app/prediction/IPredictionManager.aidl index 863fc6f952dd..94b4f5b2f30e 100644 --- a/core/java/android/app/prediction/IPredictionManager.aidl +++ b/core/java/android/app/prediction/IPredictionManager.aidl @@ -22,6 +22,7 @@ import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; import android.app.prediction.IPredictionCallback; import android.content.pm.ParceledListSlice; +import android.os.IRemoteCallback; /** * @hide @@ -48,4 +49,6 @@ interface IPredictionManager { void requestPredictionUpdate(in AppPredictionSessionId sessionId); void onDestroyPredictionSession(in AppPredictionSessionId sessionId); + + void requestServiceFeatures(in AppPredictionSessionId sessionId, in IRemoteCallback callback); } diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java index a2ffa5d34219..2402cfda3fe1 100644 --- a/core/java/android/service/appprediction/AppPredictionService.java +++ b/core/java/android/service/appprediction/AppPredictionService.java @@ -18,6 +18,7 @@ package android.service.appprediction; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.CallSuper; +import android.annotation.FlaggedApi; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,12 +32,15 @@ import android.app.prediction.AppTargetId; import android.app.prediction.IPredictionCallback; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.RemoteException; import android.service.appprediction.IPredictionService.Stub; +import android.service.appprediction.flags.Flags; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; @@ -134,6 +138,16 @@ public abstract class AppPredictionService extends Service { obtainMessage(AppPredictionService::doDestroyPredictionSession, AppPredictionService.this, sessionId)); } + + @FlaggedApi(Flags.FLAG_SERVICE_FEATURES_API) + @Override + public void requestServiceFeatures(AppPredictionSessionId sessionId, + IRemoteCallback callback) { + mHandler.sendMessage( + obtainMessage(AppPredictionService::onRequestServiceFeatures, + AppPredictionService.this, sessionId, + new RemoteCallbackWrapper(callback, null))); + } }; @CallSuper @@ -277,6 +291,18 @@ public abstract class AppPredictionService extends Service { public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {} /** + * Called by the client app to request {@link AppPredictionService} features info. + * + * @param sessionId the session's Id. It is @NonNull. + * @param callback the callback to return the Bundle which includes service features info. It + * is @NonNull. + */ + @FlaggedApi(Flags.FLAG_SERVICE_FEATURES_API) + @MainThread + public void onRequestServiceFeatures(@NonNull AppPredictionSessionId sessionId, + @NonNull Consumer<Bundle> callback) {} + + /** * Used by the prediction factory to send back results the client app. The can be called * in response to {@link #onRequestPredictionUpdate(AppPredictionSessionId)} or proactively as * a result of changes in predictions. @@ -357,4 +383,50 @@ public abstract class AppPredictionService extends Service { } } } + + private static final class RemoteCallbackWrapper implements Consumer<Bundle>, + IBinder.DeathRecipient { + + private IRemoteCallback mCallback; + private final Consumer<RemoteCallbackWrapper> mOnBinderDied; + + RemoteCallbackWrapper(IRemoteCallback callback, + @Nullable Consumer<RemoteCallbackWrapper> onBinderDied) { + mCallback = callback; + mOnBinderDied = onBinderDied; + if (mOnBinderDied != null) { + try { + mCallback.asBinder().linkToDeath(this, 0); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to link to death: " + e); + } + } + } + + public void destroy() { + if (mCallback != null && mOnBinderDied != null) { + mCallback.asBinder().unlinkToDeath(this, 0); + } + } + + @Override + public void accept(Bundle bundle) { + try { + if (mCallback != null) { + mCallback.sendResult(bundle); + } + } catch (RemoteException e) { + Slog.e(TAG, "Error sending result:" + e); + } + } + + @Override + public void binderDied() { + destroy(); + mCallback = null; + if (mOnBinderDied != null) { + mOnBinderDied.accept(this); + } + } + } } diff --git a/core/java/android/service/appprediction/IPredictionService.aidl b/core/java/android/service/appprediction/IPredictionService.aidl index 0f3df8561743..e144dfa4b22c 100644 --- a/core/java/android/service/appprediction/IPredictionService.aidl +++ b/core/java/android/service/appprediction/IPredictionService.aidl @@ -22,6 +22,7 @@ import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; import android.app.prediction.IPredictionCallback; import android.content.pm.ParceledListSlice; +import android.os.IRemoteCallback; /** * Interface from the system to a prediction service. @@ -50,4 +51,6 @@ oneway interface IPredictionService { void requestPredictionUpdate(in AppPredictionSessionId sessionId); void onDestroyPredictionSession(in AppPredictionSessionId sessionId); + + void requestServiceFeatures(in AppPredictionSessionId sessionId, in IRemoteCallback callback); } diff --git a/core/java/android/service/appprediction/flags/flags.aconfig b/core/java/android/service/appprediction/flags/flags.aconfig new file mode 100644 index 000000000000..c7e47d4b3627 --- /dev/null +++ b/core/java/android/service/appprediction/flags/flags.aconfig @@ -0,0 +1,8 @@ +package: "android.service.appprediction.flags" + +flag { + name: "service_features_api" + namespace: "systemui" + description: "Guards the new requestServiceFeatures api" + bug: "292565550" +}
\ No newline at end of file diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java index 2c5038940e98..df4e699e3926 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java @@ -35,6 +35,7 @@ import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.Binder; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.Process; import android.os.ResultReceiver; import android.os.ShellCallback; @@ -162,6 +163,13 @@ public class AppPredictionManagerService extends (service) -> service.onDestroyPredictionSessionLocked(sessionId)); } + @Override + public void requestServiceFeatures(@NonNull AppPredictionSessionId sessionId, + IRemoteCallback callback) { + runForUserLocked("requestServiceFeatures", sessionId, + (service) -> service.requestServiceFeaturesLocked(sessionId, callback)); + } + public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java index 84707a8d9c00..a0198f2fe240 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java @@ -31,6 +31,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.provider.DeviceConfig; @@ -237,6 +238,18 @@ public class AppPredictionPerUserService extends sessionInfo.destroy(); } + /** + * Requests the service to provide AppPredictionService features info. + */ + @GuardedBy("mLock") + public void requestServiceFeaturesLocked(@NonNull AppPredictionSessionId sessionId, + @NonNull IRemoteCallback callback) { + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (sessionInfo == null) return; + resolveService(sessionId, true, sessionInfo.mUsesPeopleService, + s -> s.requestServiceFeatures(sessionId, callback)); + } + @Override public void onFailureOrTimeout(boolean timedOut) { if (isDebug()) { diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 885ed355011b..b9f00d7979c2 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -38,6 +38,7 @@ import android.content.pm.ShortcutInfo; import android.os.Binder; import android.os.CancellationSignal; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -474,6 +475,10 @@ public class PeopleService extends SystemService { getDataManager().restore(userId, payload); } + @Override + public void requestServiceFeatures(AppPredictionSessionId sessionId, + IRemoteCallback callback) {} + @VisibleForTesting SessionInfo getSessionInfo(AppPredictionSessionId sessionId) { return mSessions.get(sessionId); |