Index: components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java |
diff --git a/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java b/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java |
index a984a24931749a18ed38e2886bd1c28b3ff1d8a0..2600500e0e83ee7f82178dfe3995d019574d4884 100644 |
--- a/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java |
+++ b/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java |
@@ -14,6 +14,7 @@ import org.chromium.base.annotations.CalledByNative; |
import org.chromium.base.annotations.JNINamespace; |
import java.io.IOException; |
+import java.util.concurrent.ExecutionException; |
/** |
* Wraps InstanceID and InstanceIDWithSubtype so they can be used over JNI. |
@@ -25,6 +26,8 @@ public class InstanceIDBridge { |
private final InstanceID mInstanceID; |
private long mNativeInstanceIDAndroid; |
+ private static boolean sBlockOnAsyncTasksForTesting = false; |
+ |
private InstanceIDBridge( |
long nativeInstanceIDAndroid, Context context, String subtype) { |
mInstanceID = InstanceIDWithSubtype.getInstance(context, subtype); |
@@ -51,6 +54,18 @@ public class InstanceIDBridge { |
mNativeInstanceIDAndroid = 0; |
} |
+ /** |
+ * If enabled, methods that are usually asynchronous will block returning the control flow to |
+ * C++ until the asynchronous Java operation has completed. Used this in tests where the Java |
+ * message loop is not nested. The caller is expected to reset this to false when tearing down. |
+ */ |
+ @CalledByNative |
+ private static boolean setBlockOnAsyncTasksForTesting(boolean block) { |
+ boolean wasBlocked = sBlockOnAsyncTasksForTesting; |
+ sBlockOnAsyncTasksForTesting = block; |
+ return wasBlocked; |
+ } |
+ |
/** Wrapper for {@link InstanceID#getId}. */ |
@CalledByNative |
public String getId() { |
@@ -74,9 +89,9 @@ public class InstanceIDBridge { |
for (int i = 0; i < extrasStrings.length; i += 2) { |
extras.putString(extrasStrings[i], extrasStrings[i + 1]); |
} |
- new AsyncTask<Void, Void, String>() { |
+ new BridgeAsyncTask<String>() { |
@Override |
- protected String doInBackground(Void... params) { |
+ protected String doBackgroundWork() { |
try { |
return mInstanceID.getToken(authorizedEntity, scope, extras); |
} catch (IOException ex) { |
@@ -84,10 +99,8 @@ public class InstanceIDBridge { |
} |
} |
@Override |
- protected void onPostExecute(String token) { |
- if (mNativeInstanceIDAndroid != 0) { |
- nativeDidGetToken(mNativeInstanceIDAndroid, requestId, token); |
- } |
+ protected void sendResultToNative(String token) { |
+ nativeDidGetToken(mNativeInstanceIDAndroid, requestId, token); |
} |
}.execute(); |
} |
@@ -96,9 +109,9 @@ public class InstanceIDBridge { |
@CalledByNative |
private void deleteToken( |
final int requestId, final String authorizedEntity, final String scope) { |
- new AsyncTask<Void, Void, Boolean>() { |
+ new BridgeAsyncTask<Boolean>() { |
@Override |
- protected Boolean doInBackground(Void... params) { |
+ protected Boolean doBackgroundWork() { |
try { |
mInstanceID.deleteToken(authorizedEntity, scope); |
return true; |
@@ -107,10 +120,8 @@ public class InstanceIDBridge { |
} |
} |
@Override |
- protected void onPostExecute(Boolean success) { |
- if (mNativeInstanceIDAndroid != 0) { |
- nativeDidDeleteToken(mNativeInstanceIDAndroid, requestId, success); |
- } |
+ protected void sendResultToNative(Boolean success) { |
+ nativeDidDeleteToken(mNativeInstanceIDAndroid, requestId, success); |
} |
}.execute(); |
} |
@@ -118,9 +129,9 @@ public class InstanceIDBridge { |
/** Async wrapper for {@link InstanceID#deleteInstanceID}. */ |
@CalledByNative |
private void deleteInstanceID(final int requestId) { |
- new AsyncTask<Void, Void, Boolean>() { |
+ new BridgeAsyncTask<Boolean>() { |
@Override |
- protected Boolean doInBackground(Void... params) { |
+ protected Boolean doBackgroundWork() { |
try { |
mInstanceID.deleteInstanceID(); |
return true; |
@@ -129,10 +140,8 @@ public class InstanceIDBridge { |
} |
} |
@Override |
- protected void onPostExecute(Boolean success) { |
- if (mNativeInstanceIDAndroid != 0) { |
- nativeDidDeleteID(mNativeInstanceIDAndroid, requestId, success); |
- } |
+ protected void sendResultToNative(Boolean success) { |
+ nativeDidDeleteID(mNativeInstanceIDAndroid, requestId, success); |
} |
}.execute(); |
} |
@@ -143,4 +152,45 @@ public class InstanceIDBridge { |
long nativeInstanceIDAndroid, int requestId, boolean success); |
private native void nativeDidDeleteID( |
long nativeInstanceIDAndroid, int requestId, boolean success); |
+ |
+ /** |
+ * Custom {@link AsyncTask} wrapper. As usual, this performs work on a background thread, then |
+ * sends the result back on the UI thread. There are 3 differences: |
+ * 1. sendResultToNative will be skipped if the C++ counterpart has been destroyed. |
+ * 2. Tasks run in parallel (using THREAD_POOL_EXECUTOR) to avoid blocking other Chrome tasks. |
+ * 3. If setBlockOnAsyncTasksForTesting has been enabled, executing the task will block the UI |
+ * thread, then directly call sendResultToNative. This is a workaround for use in tests |
+ * that lack a nested Java message loop (which prevents onPostExecute from running). |
+ */ |
+ private abstract class BridgeAsyncTask<Result> { |
+ protected abstract Result doBackgroundWork(); |
+ |
+ protected abstract void sendResultToNative(Result result); |
+ |
+ public void execute() { |
+ AsyncTask<Void, Void, Result> task = new AsyncTask<Void, Void, Result>() { |
+ @Override |
+ protected Result doInBackground(Void... params) { |
+ return doBackgroundWork(); |
+ } |
+ @Override |
+ protected void onPostExecute(Result result) { |
+ if (!sBlockOnAsyncTasksForTesting && mNativeInstanceIDAndroid != 0) { |
+ sendResultToNative(result); |
+ } |
+ } |
+ }; |
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
+ if (sBlockOnAsyncTasksForTesting) { |
+ Result result; |
+ try { |
+ // Synchronously block the UI thread until doInBackground returns result. |
+ result = task.get(); |
+ } catch (InterruptedException | ExecutionException e) { |
+ throw new IllegalStateException(e); // Shouldn't happen in tests. |
+ } |
+ sendResultToNative(result); |
+ } |
+ } |
+ } |
} |