summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoshan Pius <rpius@google.com>2019-10-18 06:29:14 -0700
committerRoshan Pius <rpius@google.com>2019-10-18 07:25:41 -0700
commita62a611a831f30b4d44392a0ac8bf2c0ef5a8e7c (patch)
treedbe74fa948c2434d648d25f635751a6f8b11c60e
parent82236e070922d76b7d41f038ed076b2aeb2364dc (diff)
WifiStack: Copy over runWithScissors method
Handler.runWithScissors is a simple @hide utility in Handler that should never become part of the formal Android API surface. Copy over the method to wifi. Bug: 142809066 Test: atest com.android.server.wifi.util Change-Id: I675121bb5b4e65e0a0a3db0b9ebee7277817d7c6
-rw-r--r--service/java/com/android/server/wifi/WifiThreadRunner.java116
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiThreadRunnerTest.java4
2 files changed, 116 insertions, 4 deletions
diff --git a/service/java/com/android/server/wifi/WifiThreadRunner.java b/service/java/com/android/server/wifi/WifiThreadRunner.java
index aa79fcfa1..3a0fd048e 100644
--- a/service/java/com/android/server/wifi/WifiThreadRunner.java
+++ b/service/java/com/android/server/wifi/WifiThreadRunner.java
@@ -19,6 +19,8 @@ package com.android.server.wifi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
import android.util.Log;
import com.android.server.wifi.util.GeneralUtil.Mutable;
@@ -66,7 +68,7 @@ public class WifiThreadRunner {
@Nullable
public <T> T call(@NonNull Supplier<T> supplier, T valueToReturnOnTimeout) {
Mutable<T> result = new Mutable<>();
- boolean runWithScissorsSuccess = mHandler.runWithScissors(
+ boolean runWithScissorsSuccess = runWithScissors(mHandler,
() -> result.value = supplier.get(),
RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
if (runWithScissorsSuccess) {
@@ -87,7 +89,7 @@ public class WifiThreadRunner {
*/
public boolean run(@NonNull Runnable runnable) {
boolean runWithScissorsSuccess =
- mHandler.runWithScissors(runnable, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
+ runWithScissors(mHandler, runnable, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
if (runWithScissorsSuccess) {
return true;
} else {
@@ -105,4 +107,114 @@ public class WifiThreadRunner {
public boolean post(@NonNull Runnable runnable) {
return mHandler.post(runnable);
}
+
+ // Note: @hide methods copied from android.os.Handler
+ /**
+ * Runs the specified task synchronously.
+ * <p>
+ * If the current thread is the same as the handler thread, then the runnable
+ * runs immediately without being enqueued. Otherwise, posts the runnable
+ * to the handler and waits for it to complete before returning.
+ * </p><p>
+ * This method is dangerous! Improper use can result in deadlocks.
+ * Never call this method while any locks are held or use it in a
+ * possibly re-entrant manner.
+ * </p><p>
+ * This method is occasionally useful in situations where a background thread
+ * must synchronously await completion of a task that must run on the
+ * handler's thread. However, this problem is often a symptom of bad design.
+ * Consider improving the design (if possible) before resorting to this method.
+ * </p><p>
+ * One example of where you might want to use this method is when you just
+ * set up a Handler thread and need to perform some initialization steps on
+ * it before continuing execution.
+ * </p><p>
+ * If timeout occurs then this method returns <code>false</code> but the runnable
+ * will remain posted on the handler and may already be in progress or
+ * complete at a later time.
+ * </p><p>
+ * When using this method, be sure to use {@link Looper#quitSafely} when
+ * quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely.
+ * (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
+ * </p>
+ *
+ * @param r The Runnable that will be executed synchronously.
+ * @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
+ *
+ * @return Returns true if the Runnable was successfully executed.
+ * Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ *
+ * @hide This method is prone to abuse and should probably not be in the API.
+ * If we ever do make it part of the API, we might want to rename it to something
+ * less funny like runUnsafe().
+ */
+ private static boolean runWithScissors(@NonNull Handler handler, @NonNull Runnable r,
+ long timeout) {
+ if (r == null) {
+ throw new IllegalArgumentException("runnable must not be null");
+ }
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout must be non-negative");
+ }
+
+ if (Looper.myLooper() == handler.getLooper()) {
+ r.run();
+ return true;
+ }
+
+ BlockingRunnable br = new BlockingRunnable(r);
+ return br.postAndWait(handler, timeout);
+ }
+
+ private static final class BlockingRunnable implements Runnable {
+ private final Runnable mTask;
+ private boolean mDone;
+
+ BlockingRunnable(Runnable task) {
+ mTask = task;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mTask.run();
+ } finally {
+ synchronized (this) {
+ mDone = true;
+ notifyAll();
+ }
+ }
+ }
+
+ public boolean postAndWait(Handler handler, long timeout) {
+ if (!handler.post(this)) {
+ return false;
+ }
+
+ synchronized (this) {
+ if (timeout > 0) {
+ final long expirationTime = SystemClock.uptimeMillis() + timeout;
+ while (!mDone) {
+ long delay = expirationTime - SystemClock.uptimeMillis();
+ if (delay <= 0) {
+ return false; // timeout
+ }
+ try {
+ wait(delay);
+ } catch (InterruptedException ex) {
+ }
+ }
+ } else {
+ while (!mDone) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+ return true;
+ }
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiThreadRunnerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiThreadRunnerTest.java
index 7a051e2a8..59f2ed817 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiThreadRunnerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiThreadRunnerTest.java
@@ -85,7 +85,7 @@ public class WifiThreadRunnerTest {
@Test
public void callFailure_returnValueOnTimeout() {
- doReturn(false).when(mHandler).runWithScissors(any(), anyLong());
+ doReturn(false).when(mHandler).post(any());
Integer result = mWifiThreadRunner.call(mSupplier, VALUE_ON_TIMEOUT);
@@ -110,7 +110,7 @@ public class WifiThreadRunnerTest {
@Test
public void runFailure() {
- doReturn(false).when(mHandler).runWithScissors(any(), anyLong());
+ doReturn(false).when(mHandler).post(any());
boolean runSuccess = mWifiThreadRunner.run(mRunnable);