summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt3
-rw-r--r--core/java/android/annotation/CallbackExecutor.java41
-rw-r--r--core/java/android/annotation/Condemned.java44
-rw-r--r--core/java/android/app/ActivityThread.java7
-rw-r--r--core/java/android/app/ContextImpl.java6
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java20
-rw-r--r--core/java/android/content/Context.java12
-rw-r--r--core/java/android/content/ContextWrapper.java8
-rw-r--r--core/java/android/os/HandlerExecutor.java45
-rw-r--r--test-mock/src/android/test/mock/MockContext.java11
10 files changed, 189 insertions, 8 deletions
diff --git a/api/current.txt b/api/current.txt
index dd102f6563d9..1a67e606e434 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6313,7 +6313,7 @@ package android.app.admin {
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 @@ package android.content {
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 000000000000..5671a3d2b6d6
--- /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 000000000000..186409b3e204
--- /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 5369adf4aad1..90f10c9c2934 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -80,6 +80,7 @@ import android.os.DropBoxManager;
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.Locale;
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 @@ public final class ActivityThread extends ClientTransactionHandler {
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 @@ public final class ActivityThread extends ClientTransactionHandler {
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 b0d020a7e328..a2de0f44962a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -90,6 +90,7 @@ import java.io.InputStream;
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 @@ class ContextImpl extends Context {
}
@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 cfff3614a8e4..513e7f82af55 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.ProxyInfo;
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.Arrays;
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 @@ public class DevicePolicyManager {
}
}
+ /** {@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 @@ public class DevicePolicyManager {
* @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 a47433093988..458ba05015ba 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -51,6 +51,7 @@ import android.net.Uri;
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.IOException;
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 class Context {
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 85acdc6b8101..67de4fe6bc4b 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -45,6 +45,7 @@ import java.io.FileNotFoundException;
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 class ContextWrapper extends Context {
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 000000000000..416b24b5ed05
--- /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 5e5ba462cfca..4dfd0507f351 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -19,13 +19,12 @@ package android.test.mock;
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.Handler;
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.FileNotFoundException;
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 @@ public class MockContext extends Context {
}
@Override
+ public Executor getMainExecutor() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public Context getApplicationContext() {
throw new UnsupportedOperationException();
}