Ensure some calls are available for testing
Bug: 122694646
Test: atest CtsAppPredictionServiceTestCases:AppPredictionServiceTest
Change-Id: I4534eabf8ec6365cd49117f567e25eadf3f63407
diff --git a/api/system-current.txt b/api/system-current.txt
index 328b6d2..9b297c9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -969,6 +969,7 @@
}
public static final class AppPredictionContext.Builder {
+ ctor public AppPredictionContext.Builder(@NonNull android.content.Context);
method public android.app.prediction.AppPredictionContext build();
method public android.app.prediction.AppPredictionContext.Builder setExtras(@Nullable android.os.Bundle);
method public android.app.prediction.AppPredictionContext.Builder setPredictedTargetCount(int);
@@ -1000,6 +1001,8 @@
}
public final class AppTarget implements android.os.Parcelable {
+ ctor public AppTarget(@NonNull android.app.prediction.AppTargetId, @NonNull String, @Nullable String, @NonNull android.os.UserHandle);
+ ctor public AppTarget(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo, @Nullable String);
method public int describeContents();
method @Nullable public String getClassName();
method @NonNull public android.app.prediction.AppTargetId getId();
@@ -1030,6 +1033,7 @@
}
public final class AppTargetId implements android.os.Parcelable {
+ ctor public AppTargetId(@NonNull String);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetId> CREATOR;
diff --git a/api/test-current.txt b/api/test-current.txt
index 049e002..02f0bc8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -330,6 +330,91 @@
}
+package android.app.prediction {
+
+ public final class AppPredictionContext implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public String getPackageName();
+ method public int getPredictedTargetCount();
+ method public String getUiSurface();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.prediction.AppPredictionContext> CREATOR;
+ }
+
+ public static final class AppPredictionContext.Builder {
+ ctor public AppPredictionContext.Builder(@NonNull android.content.Context);
+ method public android.app.prediction.AppPredictionContext build();
+ method public android.app.prediction.AppPredictionContext.Builder setExtras(@Nullable android.os.Bundle);
+ method public android.app.prediction.AppPredictionContext.Builder setPredictedTargetCount(int);
+ method public android.app.prediction.AppPredictionContext.Builder setUiSurface(@Nullable String);
+ }
+
+ public final class AppPredictionManager {
+ method public android.app.prediction.AppPredictor createAppPredictionSession(@NonNull android.app.prediction.AppPredictionContext);
+ }
+
+ public final class AppPredictionSessionId implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.prediction.AppPredictionSessionId> CREATOR;
+ }
+
+ public final class AppPredictor {
+ method public void destroy();
+ method public android.app.prediction.AppPredictionSessionId getSessionId();
+ method public void notifyAppTargetEvent(@NonNull android.app.prediction.AppTargetEvent);
+ method public void notifyLocationShown(@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 @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);
+ }
+
+ public static interface AppPredictor.Callback {
+ method public void onTargetsAvailable(@NonNull java.util.List<android.app.prediction.AppTarget>);
+ }
+
+ public final class AppTarget implements android.os.Parcelable {
+ ctor public AppTarget(@NonNull android.app.prediction.AppTargetId, @NonNull String, @Nullable String, @NonNull android.os.UserHandle);
+ method public int describeContents();
+ method @Nullable public String getClassName();
+ method @NonNull public android.app.prediction.AppTargetId getId();
+ method @NonNull public String getPackageName();
+ method public int getRank();
+ method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
+ method @NonNull public android.os.UserHandle getUser();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.prediction.AppTarget> CREATOR;
+ }
+
+ public final class AppTargetEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public int getAction();
+ method @NonNull public String getLaunchLocation();
+ method @Nullable public android.app.prediction.AppTarget getTarget();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ACTION_DISMISS = 2; // 0x2
+ field public static final int ACTION_LAUNCH = 1; // 0x1
+ field public static final int ACTION_PIN = 3; // 0x3
+ field public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetEvent> CREATOR;
+ }
+
+ public static final class AppTargetEvent.Builder {
+ ctor public AppTargetEvent.Builder(@Nullable android.app.prediction.AppTarget, int);
+ method public android.app.prediction.AppTargetEvent build();
+ method public android.app.prediction.AppTargetEvent.Builder setLaunchLocation(String);
+ }
+
+ public final class AppTargetId implements android.os.Parcelable {
+ ctor public AppTargetId(@NonNull String);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetId> CREATOR;
+ }
+
+}
+
package android.app.role {
public final class RoleManager {
@@ -1686,6 +1771,24 @@
}
+package android.service.appprediction {
+
+ public abstract class AppPredictionService extends android.app.Service {
+ ctor public AppPredictionService();
+ method @MainThread public abstract void onAppTargetEvent(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull android.app.prediction.AppTargetEvent);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onCreatePredictionSession(@NonNull android.app.prediction.AppPredictionContext, @NonNull android.app.prediction.AppPredictionSessionId);
+ method @MainThread public void onDestroyPredictionSession(@NonNull android.app.prediction.AppPredictionSessionId);
+ method @MainThread public abstract void onLocationShown(@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 @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();
+ method public final void updatePredictions(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull java.util.List<android.app.prediction.AppTarget>);
+ }
+
+}
+
package android.service.autofill {
public abstract class AutofillFieldClassificationService extends android.app.Service {
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
index 87ccb66..2fbe6e36 100644
--- a/core/java/android/app/prediction/AppPredictionContext.java
+++ b/core/java/android/app/prediction/AppPredictionContext.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcel;
@@ -29,6 +30,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class AppPredictionContext implements Parcelable {
private final int mPredictedTargetCount;
@@ -73,6 +75,17 @@
}
@Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!getClass().equals(o != null ? o.getClass() : null)) return false;
+
+ AppPredictionContext other = (AppPredictionContext) o;
+ return mPredictedTargetCount == other.mPredictedTargetCount
+ && mUiSurface.equals(other.mUiSurface)
+ && mPackageName.equals(other.mPackageName);
+ }
+
+ @Override
public int describeContents() {
return 0;
}
@@ -104,6 +117,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final class Builder {
@NonNull
@@ -116,8 +130,12 @@
private Bundle mExtras;
/**
+ * TODO(b/123591863): Add java docs
+ *
* @hide
*/
+ @SystemApi
+ @TestApi
public Builder(@NonNull Context context) {
mPackageName = context.getPackageName();
}
diff --git a/core/java/android/app/prediction/AppPredictionManager.java b/core/java/android/app/prediction/AppPredictionManager.java
index f8578d4..99f78f1 100644
--- a/core/java/android/app/prediction/AppPredictionManager.java
+++ b/core/java/android/app/prediction/AppPredictionManager.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Context;
import com.android.internal.util.Preconditions;
@@ -26,6 +27,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class AppPredictionManager {
private final Context mContext;
diff --git a/core/java/android/app/prediction/AppPredictionSessionId.java b/core/java/android/app/prediction/AppPredictionSessionId.java
index 1d7308e..1e76c24 100644
--- a/core/java/android/app/prediction/AppPredictionSessionId.java
+++ b/core/java/android/app/prediction/AppPredictionSessionId.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,6 +27,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class AppPredictionSessionId implements Parcelable {
private final String mId;
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index 2ddbd08c..12d6ce3 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -19,6 +19,7 @@
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;
@@ -63,6 +64,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class AppPredictor {
private static final String TAG = AppPredictor.class.getSimpleName();
@@ -102,6 +104,10 @@
* Notifies the prediction service of an 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) {
@@ -114,6 +120,10 @@
*/
public void notifyLocationShown(@NonNull String launchLocation,
@NonNull List<AppTargetId> targetIds) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
try {
mPredictionManager.notifyLocationShown(mSessionId, launchLocation,
new ParceledListSlice<>(targetIds));
@@ -130,6 +140,10 @@
*/
public void registerPredictionUpdates(@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull AppPredictor.Callback callback) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
if (mRegisteredCallbacks.containsKey(callback)) {
// Skip if this callback is already registered
return;
@@ -149,6 +163,10 @@
* callback until the callback is re-registered.
*/
public void unregisterPredictionUpdates(@NonNull AppPredictor.Callback callback) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
if (!mRegisteredCallbacks.containsKey(callback)) {
// Skip if this callback was never registered
return;
@@ -168,6 +186,10 @@
* @see Callback#onTargetsAvailable(List)
*/
public void requestPredictionUpdate() {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
try {
mPredictionManager.requestPredictionUpdate(mSessionId);
} catch (RemoteException e) {
@@ -182,6 +204,10 @@
@Nullable
public void sortTargets(@NonNull List<AppTarget> targets,
@NonNull Executor callbackExecutor, @NonNull Consumer<List<AppTarget>> callback) {
+ if (mIsClosed.get()) {
+ throw new IllegalStateException("This client has already been destroyed.");
+ }
+
try {
mPredictionManager.sortAppTargets(mSessionId, new ParceledListSlice(targets),
new CallbackWrapper(callbackExecutor, callback));
@@ -206,6 +232,8 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify app target event", e);
}
+ } else {
+ throw new IllegalStateException("This client has already been destroyed.");
}
}
@@ -222,6 +250,16 @@
}
/**
+ * TODO(b/123591863): Add java docs
+ *
+ * @hide
+ */
+ @TestApi
+ public AppPredictionSessionId getSessionId() {
+ return mSessionId;
+ }
+
+ /**
* Callback for receiving prediction updates.
*/
public interface Callback {
diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java
index 99c1c44..b924cec 100644
--- a/core/java/android/app/prediction/AppTarget.java
+++ b/core/java/android/app/prediction/AppTarget.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.pm.ShortcutInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,6 +31,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class AppTarget implements Parcelable {
private final AppTargetId mId;
@@ -42,8 +44,12 @@
private int mRank;
/**
+ * TODO(b/123591863): Add java docs
+ *
* @hide
*/
+ @SystemApi
+ @TestApi
public AppTarget(@NonNull AppTargetId id, @NonNull String packageName,
@Nullable String className, @NonNull UserHandle user) {
mId = id;
@@ -55,15 +61,19 @@
}
/**
+ * TODO(b/123591863): Add java docs
+ *
* @hide
*/
- public AppTarget(@NonNull AppTargetId id, @NonNull ShortcutInfo shortcutInfo) {
+ @SystemApi
+ public AppTarget(@NonNull AppTargetId id, @NonNull ShortcutInfo shortcutInfo,
+ @Nullable String className) {
mId = id;
mShortcutInfo = Preconditions.checkNotNull(shortcutInfo);
mPackageName = mShortcutInfo.getPackage();
mUser = mShortcutInfo.getUserHandle();
- mClassName = null;
+ mClassName = className;
}
private AppTarget(Parcel parcel) {
@@ -71,13 +81,12 @@
mShortcutInfo = parcel.readTypedObject(ShortcutInfo.CREATOR);
if (mShortcutInfo == null) {
mPackageName = parcel.readString();
- mClassName = parcel.readString();
mUser = UserHandle.of(parcel.readInt());
} else {
mPackageName = mShortcutInfo.getPackage();
mUser = mShortcutInfo.getUserHandle();
- mClassName = null;
}
+ mClassName = parcel.readString();
mRank = parcel.readInt();
}
@@ -129,11 +138,32 @@
mRank = rank;
}
+ /**
+ * Returns the rank for the target.
+ */
public int getRank() {
return mRank;
}
@Override
+ public boolean equals(Object o) {
+ if (!getClass().equals(o != null ? o.getClass() : null)) return false;
+
+ AppTarget other = (AppTarget) o;
+ boolean sameClassName = (mClassName == null && other.mClassName == null)
+ || (mClassName != null && mClassName.equals(other.mClassName));
+ boolean sameShortcutInfo = (mShortcutInfo == null && other.mShortcutInfo == null)
+ || (mShortcutInfo != null && other.mShortcutInfo != null
+ && mShortcutInfo.getId() == other.mShortcutInfo.getId());
+ return mId.equals(other.mId)
+ && mPackageName.equals(other.mPackageName)
+ && sameClassName
+ && mUser.equals(other.mUser)
+ && sameShortcutInfo
+ && mRank == other.mRank;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
@@ -144,9 +174,9 @@
dest.writeTypedObject(mShortcutInfo, flags);
if (mShortcutInfo == null) {
dest.writeString(mPackageName);
- dest.writeString(mClassName);
dest.writeInt(mUser.getIdentifier());
}
+ dest.writeString(mClassName);
dest.writeInt(mRank);
}
diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java
index 18317e1..37e41de 100644
--- a/core/java/android/app/prediction/AppTargetEvent.java
+++ b/core/java/android/app/prediction/AppTargetEvent.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,6 +31,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class AppTargetEvent implements Parcelable {
/**
@@ -96,6 +98,16 @@
}
@Override
+ public boolean equals(Object o) {
+ if (!getClass().equals(o != null ? o.getClass() : null)) return false;
+
+ AppTargetEvent other = (AppTargetEvent) o;
+ return mTarget.equals(other.mTarget)
+ && mLocation.equals(other.mLocation)
+ && mAction == other.mAction;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
@@ -126,6 +138,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final class Builder {
private AppTarget mTarget;
private String mLocation;
diff --git a/core/java/android/app/prediction/AppTargetId.java b/core/java/android/app/prediction/AppTargetId.java
index 0b8fb47..639ba78 100644
--- a/core/java/android/app/prediction/AppTargetId.java
+++ b/core/java/android/app/prediction/AppTargetId.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,14 +26,19 @@
* @hide
*/
@SystemApi
+@TestApi
public final class AppTargetId implements Parcelable {
@NonNull
private final String mId;
/**
+ * TODO(b/123591863): Add java docs
+ *
* @hide
*/
+ @SystemApi
+ @TestApi
public AppTargetId(@NonNull String id) {
mId = id;
}
diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java
index b77405a..d012851 100644
--- a/core/java/android/service/appprediction/AppPredictionService.java
+++ b/core/java/android/service/appprediction/AppPredictionService.java
@@ -21,6 +21,7 @@
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
@@ -49,6 +50,7 @@
* @hide
*/
@SystemApi
+@TestApi
public abstract class AppPredictionService extends Service {
private static final String TAG = "AppPredictionService";
@@ -140,6 +142,7 @@
@Override
public final IBinder onBind(Intent intent) {
+ // TODO(b/111701043): Verify that the action is valid
return mInterface.asBinder();
}
@@ -228,6 +231,7 @@
public void onStopPredictionUpdates() {}
private void doRequestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
+ // Just an optimization, if there are no callbacks, then don't bother notifying the service
final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
if (callbacks != null && !callbacks.isEmpty()) {
onRequestPredictionUpdate(sessionId);