/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.prediction; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; 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; import com.android.internal.annotations.GuardedBy; import dalvik.system.CloseGuard; import java.util.List; import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; /** * Class that represents an App Prediction client. * *
* Usage:
{@code * * class MyActivity { * private AppPredictor mClient * * void onCreate() { * mClient = new AppPredictor(...) * mClient.registerPredictionUpdates(...) * } * * void onStart() { * mClient.requestPredictionUpdate() * } * * void onClick(...) { * mClient.notifyAppTargetEvent(...) * } * * void onDestroy() { * mClient.unregisterPredictionUpdates() * mClient.close() * } * * }* * @hide */ @SystemApi public final class AppPredictor { private static final String TAG = AppPredictor.class.getSimpleName(); private final IPredictionManager mPredictionManager; private final CloseGuard mCloseGuard = CloseGuard.get(); private final AtomicBoolean mIsClosed = new AtomicBoolean(false); private final AppPredictionSessionId mSessionId; @GuardedBy("itself") private final ArrayMap
* The caller should call {@link AppPredictor#destroy()} to dispose the client once it
* no longer used.
*
* @param context The {@link Context} of the user of this {@link AppPredictor}.
* @param predictionContext The prediction context.
*/
AppPredictor(@NonNull Context context, @NonNull AppPredictionContext predictionContext) {
IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE);
mPredictionManager = IPredictionManager.Stub.asInterface(b);
mSessionId = new AppPredictionSessionId(
context.getPackageName() + ":" + UUID.randomUUID(), context.getUserId());
try {
mPredictionManager.createPredictionSession(predictionContext, mSessionId, getToken());
} catch (RemoteException e) {
Log.e(TAG, "Failed to create predictor", e);
e.rethrowAsRuntimeException();
}
mCloseGuard.open("AppPredictor.close");
}
/**
* Notifies the prediction service of an app target event.
*
* @param event The {@link AppTargetEvent} that represents the app target event.
*/
public void notifyAppTargetEvent(@NonNull AppTargetEvent event) {
if (mIsClosed.get()) {
throw new IllegalStateException("This client has already been destroyed.");
}
try {
mPredictionManager.notifyAppTargetEvent(mSessionId, event);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify app target event", e);
e.rethrowAsRuntimeException();
}
}
/**
* Notifies the prediction service when the targets in a launch location are shown to the user.
*
* @param launchLocation The launch location where the targets are shown to the user.
* @param targetIds List of {@link AppTargetId}s that are shown to the user.
*/
public void notifyLaunchLocationShown(@NonNull String launchLocation,
@NonNull List> callback) {
if (mIsClosed.get()) {
throw new IllegalStateException("This client has already been destroyed.");
}
try {
mPredictionManager.sortAppTargets(mSessionId, new ParceledListSlice<>(targets),
new CallbackWrapper(callbackExecutor, callback));
} catch (RemoteException e) {
Log.e(TAG, "Failed to sort targets", e);
e.rethrowAsRuntimeException();
}
}
/**
* 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
> mCallback;
private final Executor mExecutor;
CallbackWrapper(@NonNull Executor callbackExecutor,
@NonNull Consumer
> callback) {
mCallback = callback;
mExecutor = callbackExecutor;
}
@Override
public void onResult(ParceledListSlice result) {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> mCallback.accept(result.getList()));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
static class RemoteCallbackWrapper extends IRemoteCallback.Stub {
private final Consumer