[fixed] Replace RemoteCallback with AndroidFuture in PermControler

This is ag/7529281 + fix ag/7572218

Test: atest AddConfigWidgetTest#testConfigCancelled
atest android.permission.cts.PermissionControllerTest
Change-Id: I9d8f28c0665a7ae4040ea471ed6a0187628a0306
diff --git a/core/java/com/android/internal/infra/AndroidFuture.aidl b/core/java/com/android/internal/infra/AndroidFuture.aidl
new file mode 100644
index 0000000..b19aab8
--- /dev/null
+++ b/core/java/com/android/internal/infra/AndroidFuture.aidl
@@ -0,0 +1,20 @@
+/*
+** Copyright 2019, 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 com.android.internal.infra;
+
+/** @hide */
+parcelable AndroidFuture;
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index c9e2d5f..08938a6 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -23,6 +23,9 @@
 import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
 import android.util.ExceptionUtils;
 import android.util.Log;
 
@@ -45,6 +48,7 @@
  * A customized {@link CompletableFuture} with focus on reducing the number of allocations involved
  * in a typical future usage scenario for Android.
  *
+ * <p>
  * In particular this involves allocations optimizations in:
  * <ul>
  *     <li>{@link #thenCompose(Function)}</li>
@@ -55,10 +59,20 @@
  * </ul>
  * As well as their *Async versions.
  *
+ * <p>
+ * You can pass {@link AndroidFuture} across an IPC.
+ * When doing so, completing the future on the other side will propagate the completion back,
+ * effectively acting as an error-aware remote callback.
+ *
+ * <p>
+ * {@link AndroidFuture} is {@link Parcelable} iff its wrapped type {@code T} is
+ * effectively parcelable, i.e. is supported by {@link Parcel#readValue}/{@link Parcel#writeValue}.
+ *
  * @param <T> see {@link CompletableFuture}
  */
-public class AndroidFuture<T> extends CompletableFuture<T> {
+public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable {
 
+    private static final boolean DEBUG = false;
     private static final String LOG_TAG = AndroidFuture.class.getSimpleName();
 
     private final @NonNull Object mLock = new Object();
@@ -67,6 +81,38 @@
     @GuardedBy("mLock")
     private @Nullable Executor mListenerExecutor = DIRECT_EXECUTOR;
     private @NonNull Handler mTimeoutHandler = Handler.getMain();
+    private final @Nullable IAndroidFuture mRemoteOrigin;
+
+    public AndroidFuture() {
+        super();
+        mRemoteOrigin = null;
+    }
+
+    AndroidFuture(Parcel in) {
+        super();
+        if (in.readBoolean()) {
+            // Done
+            if (in.readBoolean()) {
+                // Failed
+                try {
+                    in.readException();
+                } catch (Throwable e) {
+                    completeExceptionally(e);
+                }
+                if (!isCompletedExceptionally()) {
+                    throw new IllegalStateException(
+                            "Error unparceling AndroidFuture: exception expected");
+                }
+            } else {
+                // Success
+                complete((T) in.readValue(null));
+            }
+            mRemoteOrigin = null;
+        } else {
+            // Not done
+            mRemoteOrigin = IAndroidFuture.Stub.asInterface(in.readStrongBinder());
+        }
+    }
 
     @Override
     public boolean complete(@Nullable T value) {
@@ -90,6 +136,11 @@
     protected void onCompleted(@Nullable T res, @Nullable Throwable err) {
         cancelTimeout();
 
+        if (DEBUG) {
+            Log.i(LOG_TAG, this + " completed with result " + (err == null ? res : err),
+                    new RuntimeException());
+        }
+
         BiConsumer<? super T, ? super Throwable> listener;
         synchronized (mLock) {
             listener = mListener;
@@ -99,6 +150,14 @@
         if (listener != null) {
             callListenerAsync(listener, res, err);
         }
+
+        if (mRemoteOrigin != null) {
+            try {
+                mRemoteOrigin.complete(this /* resultContainer */);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Failed to propagate completion", e);
+            }
+        }
     }
 
     @Override
@@ -413,4 +472,49 @@
             }
         }
     }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        boolean done = isDone();
+        dest.writeBoolean(done);
+        if (done) {
+            T result;
+            try {
+                result = get();
+            } catch (Exception t) {
+                dest.writeBoolean(true);
+                dest.writeException(t);
+                return;
+            }
+            dest.writeBoolean(false);
+            dest.writeValue(result);
+        } else {
+            dest.writeStrongBinder(new IAndroidFuture.Stub() {
+                @Override
+                public void complete(AndroidFuture resultContainer) {
+                    try {
+                        AndroidFuture.this.complete((T) resultContainer.get());
+                    } catch (Throwable t) {
+                        completeExceptionally(t);
+                    }
+                }
+            }.asBinder());
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Parcelable.Creator<AndroidFuture> CREATOR =
+            new Parcelable.Creator<AndroidFuture>() {
+                public AndroidFuture createFromParcel(Parcel parcel) {
+                    return new AndroidFuture(parcel);
+                }
+
+                public AndroidFuture[] newArray(int size) {
+                    return new AndroidFuture[size];
+                }
+            };
 }
diff --git a/core/java/com/android/internal/infra/IAndroidFuture.aidl b/core/java/com/android/internal/infra/IAndroidFuture.aidl
new file mode 100644
index 0000000..302fdde
--- /dev/null
+++ b/core/java/com/android/internal/infra/IAndroidFuture.aidl
@@ -0,0 +1,23 @@
+/*
+** Copyright 2019, 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 com.android.internal.infra;
+
+import com.android.internal.infra.AndroidFuture;
+
+oneway interface IAndroidFuture {
+    void complete(in AndroidFuture resultContainer);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 78fdfe4..f9cf23b 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.ExceptionUtils;
 
@@ -29,6 +30,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
@@ -328,6 +330,33 @@
     }
 
     /**
+     * Applies {@code action} to each element in {@code cur}
+     *
+     * This avoids creating an iterator if the given map is an {@link ArrayMap}
+     * For non-{@link ArrayMap}s it avoids creating {@link Map.Entry} instances
+     */
+    public static <K, V> void forEach(@Nullable Map<K, V> cur, @Nullable BiConsumer<K, V> action) {
+        if (cur == null || action == null) {
+            return;
+        }
+        int size = cur.size();
+        if (size == 0) {
+            return;
+        }
+
+        if (cur instanceof ArrayMap) {
+            ArrayMap<K, V> arrayMap = (ArrayMap<K, V>) cur;
+            for (int i = 0; i < size; i++) {
+                action.accept(arrayMap.keyAt(i), arrayMap.valueAt(i));
+            }
+        } else {
+            for (K key : cur.keySet()) {
+                action.accept(key, cur.get(key));
+            }
+        }
+    }
+
+    /**
      * @return the first element if not empty/null, null otherwise
      */
     public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) {