Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 20309fcf authored by Song Hu's avatar Song Hu Committed by Android (Google) Code Review
Browse files

Merge "Add new method 'requestServiceFeatures' in AppPredictionService. It...

Merge "Add new method 'requestServiceFeatures' in AppPredictionService. It allows Frontend to get backend service impl features info (e.g. feature readiness)" into main
parents 0d297ab7 bfd6be48
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -54,6 +54,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.systemserver.flags-aconfig-java{.generated_srcjars}",
@@ -125,6 +126,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",
@@ -726,6 +728,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",
+2 −0
Original line number Diff line number Diff line
@@ -2205,6 +2205,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);
  }
@@ -12059,6 +12060,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();
+54 −0
Original line number Diff line number Diff line
@@ -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;

@@ -262,6 +266,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);
    }
+3 −0
Original line number Diff line number Diff line
@@ -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);
}
+72 −0
Original line number Diff line number Diff line
@@ -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
@@ -276,6 +290,18 @@ public abstract class AppPredictionService extends Service {
    @MainThread
    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
@@ -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);
            }
        }
    }
}
Loading