Merge "Make AppPredictor thread-safe" into tm-qpr-dev am: 2a4339d073 am: ce2368e599
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20269815
Change-Id: If1c0ff4471ecd34ccb6cdadcc69e0b4f3521a52f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index 2581daa..d628b7f 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -30,6 +30,8 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import dalvik.system.CloseGuard;
import java.util.List;
@@ -79,6 +81,7 @@
private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
private final AppPredictionSessionId mSessionId;
+ @GuardedBy("itself")
private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
/**
@@ -94,7 +97,7 @@
IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE);
mPredictionManager = IPredictionManager.Stub.asInterface(b);
mSessionId = new AppPredictionSessionId(
- context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
+ context.getPackageName() + ":" + UUID.randomUUID(), context.getUserId());
try {
mPredictionManager.createPredictionSession(predictionContext, mSessionId, getToken());
} catch (RemoteException e) {
@@ -155,6 +158,15 @@
*/
public void registerPredictionUpdates(@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull AppPredictor.Callback callback) {
+ synchronized (mRegisteredCallbacks) {
+ registerPredictionUpdatesLocked(callbackExecutor, callback);
+ }
+ }
+
+ @GuardedBy("mRegisteredCallbacks")
+ private void registerPredictionUpdatesLocked(
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull AppPredictor.Callback callback) {
if (mIsClosed.get()) {
throw new IllegalStateException("This client has already been destroyed.");
}
@@ -183,6 +195,13 @@
* @param callback The callback to be unregistered.
*/
public void unregisterPredictionUpdates(@NonNull AppPredictor.Callback callback) {
+ synchronized (mRegisteredCallbacks) {
+ unregisterPredictionUpdatesLocked(callback);
+ }
+ }
+
+ @GuardedBy("mRegisteredCallbacks")
+ private void unregisterPredictionUpdatesLocked(@NonNull AppPredictor.Callback callback) {
if (mIsClosed.get()) {
throw new IllegalStateException("This client has already been destroyed.");
}
@@ -235,7 +254,7 @@
}
try {
- mPredictionManager.sortAppTargets(mSessionId, new ParceledListSlice(targets),
+ mPredictionManager.sortAppTargets(mSessionId, new ParceledListSlice<>(targets),
new CallbackWrapper(callbackExecutor, callback));
} catch (RemoteException e) {
Log.e(TAG, "Failed to sort targets", e);
@@ -251,19 +270,25 @@
if (!mIsClosed.getAndSet(true)) {
mCloseGuard.close();
- // Do destroy;
- try {
- mPredictionManager.onDestroyPredictionSession(mSessionId);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to notify app target event", e);
- e.rethrowAsRuntimeException();
+ synchronized (mRegisteredCallbacks) {
+ destroySessionLocked();
}
- mRegisteredCallbacks.clear();
} else {
throw new IllegalStateException("This client has already been destroyed.");
}
}
+ @GuardedBy("mRegisteredCallbacks")
+ private void destroySessionLocked() {
+ try {
+ mPredictionManager.onDestroyPredictionSession(mSessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify app target event", e);
+ e.rethrowAsRuntimeException();
+ }
+ mRegisteredCallbacks.clear();
+ }
+
@Override
protected void finalize() throws Throwable {
try {