Start accepting Executors instead of Handlers.

After discussion in API council, our new best-practices are to have
developers provide an Executor to dispatch callbacks/listeners on,
instead of the previous guidance of using a Handler.

Define Context.getMainExecutor() to easily obtain an Executor
associated with the main thread of application.  This allows new
APIs to require a @NonNull Executor.  Also define a new
@CallbackExecutor auto-doc annotation that explains background and
points developers at new Context method above.

Test: cts-tradefed run commandAndExit cts-dev -m CtsContentTestCases -t android.content.cts.ContextTest#testMainLooper
Test: cts-tradefed run commandAndExit cts-dev -m CtsContentTestCases -t android.content.cts.ContextTest#testMainExecutor
Bug: 70348426
Change-Id: I536892bcd80fcfba1bb1ddf67648c08a26d7ddd2
diff --git a/api/current.txt b/api/current.txt
index dd102f6..1a67e60 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6313,7 +6313,7 @@
     method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
     method public void addUserRestriction(android.content.ComponentName, java.lang.String);
     method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
-    method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, android.os.Handler);
+    method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, java.util.concurrent.Executor);
     method public void clearCrossProfileIntentFilters(android.content.ComponentName);
     method public deprecated void clearDeviceOwnerApp(java.lang.String);
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
@@ -9050,6 +9050,7 @@
     method public abstract java.io.File[] getExternalMediaDirs();
     method public abstract java.io.File getFileStreamPath(java.lang.String);
     method public abstract java.io.File getFilesDir();
+    method public java.util.concurrent.Executor getMainExecutor();
     method public abstract android.os.Looper getMainLooper();
     method public abstract java.io.File getNoBackupFilesDir();
     method public abstract java.io.File getObbDir();
diff --git a/core/java/android/annotation/CallbackExecutor.java b/core/java/android/annotation/CallbackExecutor.java
new file mode 100644
index 0000000..5671a3d
--- /dev/null
+++ b/core/java/android/annotation/CallbackExecutor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.annotation;
+
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.content.Context;
+import android.os.AsyncTask;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.concurrent.Executor;
+
+/**
+ * @paramDoc Callback and listener events are dispatched through this
+ *           {@link Executor}, providing an easy way to control which thread is
+ *           used. To dispatch events through the main thread of your
+ *           application, you can use {@link Context#getMainExecutor()}. To
+ *           dispatch events through a shared thread pool, you can use
+ *           {@link AsyncTask#THREAD_POOL_EXECUTOR}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target(PARAMETER)
+public @interface CallbackExecutor {
+}
diff --git a/core/java/android/annotation/Condemned.java b/core/java/android/annotation/Condemned.java
new file mode 100644
index 0000000..186409b
--- /dev/null
+++ b/core/java/android/annotation/Condemned.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A program element annotated @Condemned is one that programmers are
+ * blocked from using, typically because it's about to be completely destroyed.
+ * <p>
+ * This is a stronger version of &#64;Deprecated, and it's typically used to
+ * mark APIs that only existed temporarily in a preview SDK, and which only
+ * continue to exist temporarily to support binary compatibility.
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
+public @interface Condemned {
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5369adf..90f10c9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -80,6 +80,7 @@
 import android.os.Environment;
 import android.os.GraphicsEnvironment;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.LocaleList;
 import android.os.Looper;
@@ -172,6 +173,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.TimeZone;
+import java.util.concurrent.Executor;
 
 final class RemoteServiceException extends AndroidRuntimeException {
     public RemoteServiceException(String msg) {
@@ -242,6 +244,7 @@
     final ApplicationThread mAppThread = new ApplicationThread();
     final Looper mLooper = Looper.myLooper();
     final H mH = new H();
+    final Executor mExecutor = new HandlerExecutor(mH);
     final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
     // List of new activities (via ActivityRecord.nextIdle) that should
     // be reported when next we idle.
@@ -1943,6 +1946,10 @@
         return mLooper;
     }
 
+    public Executor getExecutor() {
+        return mExecutor;
+    }
+
     public Application getApplication() {
         return mInitialApplication;
     }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b0d020a..a2de0f4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -90,6 +90,7 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 class ReceiverRestrictedContext extends ContextWrapper {
     ReceiverRestrictedContext(Context base) {
@@ -250,6 +251,11 @@
     }
 
     @Override
+    public Executor getMainExecutor() {
+        return mMainThread.getExecutor();
+    }
+
+    @Override
     public Context getApplicationContext() {
         return (mPackageInfo != null) ?
                 mPackageInfo.getApplication() : mMainThread.getApplication();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index cfff361..513e7f8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,7 +16,9 @@
 
 package android.app.admin;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.ColorInt;
+import android.annotation.Condemned;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -49,6 +51,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -94,6 +97,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 /**
  * Public interface for managing policies enforced on a device. Most clients of this class must be
@@ -8617,6 +8621,15 @@
         }
     }
 
+    /** {@hide} */
+    @Condemned
+    @Deprecated
+    public boolean clearApplicationUserData(@NonNull ComponentName admin,
+            @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
+            @NonNull Handler handler) {
+        return clearApplicationUserData(admin, packageName, listener, new HandlerExecutor(handler));
+    }
+
     /**
      * Called by the device owner or profile owner to clear application user data of a given
      * package. The behaviour of this is equivalent to the target application calling
@@ -8628,19 +8641,20 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param packageName The name of the package which will have its user data wiped.
      * @param listener A callback object that will inform the caller when the clearing is done.
-     * @param handler The handler indicating the thread on which the listener should be invoked.
+     * @param executor The executor through which the listener should be invoked.
      * @throws SecurityException if the caller is not the device owner/profile owner.
      * @return whether the clearing succeeded.
      */
     public boolean clearApplicationUserData(@NonNull ComponentName admin,
             @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
-            @NonNull Handler handler) {
+            @NonNull @CallbackExecutor Executor executor) {
         throwIfParentInstance("clearAppData");
+        Preconditions.checkNotNull(executor);
         try {
             return mService.clearApplicationUserData(admin, packageName,
                     new IPackageDataObserver.Stub() {
                         public void onRemoveCompleted(String pkg, boolean succeeded) {
-                            handler.post(() ->
+                            executor.execute(() ->
                                     listener.onApplicationUserDataCleared(pkg, succeeded));
                         }
                     });
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a474330..458ba05 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -51,6 +51,7 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.StatFs;
@@ -75,6 +76,7 @@
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 /**
  * Interface to global information about an application environment.  This is
@@ -462,6 +464,16 @@
     public abstract Looper getMainLooper();
 
     /**
+     * Return an {@link Executor} that will run enqueued tasks on the main
+     * thread associated with this context. This is the thread used to dispatch
+     * calls to application components (activities, services, etc).
+     */
+    public Executor getMainExecutor() {
+        // This is pretty inefficient, which is why ContextImpl overrides it
+        return new HandlerExecutor(new Handler(getMainLooper()));
+    }
+
+    /**
      * Return the context of the single, global Application object of the
      * current process.  This generally should only be used if you need a
      * Context whose lifecycle is separate from the current context, that is
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 85acdc6..67de4fe 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -45,6 +45,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.concurrent.Executor;
 
 /**
  * Proxying implementation of Context that simply delegates all of its calls to
@@ -103,7 +104,12 @@
     public Looper getMainLooper() {
         return mBase.getMainLooper();
     }
-    
+
+    @Override
+    public Executor getMainExecutor() {
+        return mBase.getMainExecutor();
+    }
+
     @Override
     public Context getApplicationContext() {
         return mBase.getApplicationContext();
diff --git a/core/java/android/os/HandlerExecutor.java b/core/java/android/os/HandlerExecutor.java
new file mode 100644
index 0000000..416b24b
--- /dev/null
+++ b/core/java/android/os/HandlerExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.os;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * An adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+public class HandlerExecutor implements Executor {
+    private final Handler mHandler;
+
+    public HandlerExecutor(@NonNull Handler handler) {
+        mHandler = Preconditions.checkNotNull(handler);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        if (!mHandler.post(command)) {
+            throw new RejectedExecutionException(mHandler + " is shutting down");
+        }
+    }
+}
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 5e5ba46..4dfd050 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -19,13 +19,12 @@
 import android.annotation.SystemApi;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
-import android.app.Notification;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.BroadcastReceiver;
 import android.content.IntentSender;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
@@ -44,8 +43,8 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.UserHandle;
-import android.view.DisplayAdjustments;
 import android.view.Display;
+import android.view.DisplayAdjustments;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -53,6 +52,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.concurrent.Executor;
 
 /**
  * A mock {@link android.content.Context} class.  All methods are non-functional and throw
@@ -87,6 +87,11 @@
     }
 
     @Override
+    public Executor getMainExecutor() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public Context getApplicationContext() {
         throw new UnsupportedOperationException();
     }