TM#setNetworkSelectionModeAutomatic hang muliple minutes
Root Cause:
TM#setNetworkSelectionModeAutomatic is implemented as a blocking
operation. When modem detects abnormal context (slow network
registration, temporary failure, lack of available network ect.),
it will wait for timout (which may be up to 200 seconds) before sending
telephony the result.
The will block the MainThreadRequest in PIM to return, although the API
is not designed to return any value.
Solution:
Introduces timeout for PIM#sendRequest and wait for maximum of 2 seonds
for TM#setNetworkSelectionModeAutomatic to return.
Bug: 176239119
Test: manual smoke test
Change-Id: Ic6be6c831d770845e0fa75dbfd23e72f61f7854b
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index f1be951..5760527 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -51,6 +51,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceSpecificException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
@@ -371,6 +372,8 @@
public static final String RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED =
"reset_network_erase_modem_config_enabled";
+ private static final int SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS = 2000; // 2 seconds
+
/**
* A request object to use for transmitting data to an ICC.
*/
@@ -1890,8 +1893,8 @@
* @see #sendRequestAsync
*/
private Object sendRequest(int command, Object argument) {
- return sendRequest(
- command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, null, null);
+ return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, null,
+ null, -1 /*timeoutInMs*/);
}
/**
@@ -1901,7 +1904,7 @@
*/
private Object sendRequest(int command, Object argument, WorkSource workSource) {
return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- null, workSource);
+ null, workSource, -1 /*timeoutInMs*/);
}
/**
@@ -1910,7 +1913,18 @@
* @see #sendRequestAsync
*/
private Object sendRequest(int command, Object argument, Integer subId) {
- return sendRequest(command, argument, subId, null, null);
+ return sendRequest(command, argument, subId, null, null, -1 /*timeoutInMs*/);
+ }
+
+ /**
+ * Posts the specified command to be executed on the main thread,
+ * waits for the request to complete for at most {@code timeoutInMs}, and returns the result
+ * if not timeout or null otherwise.
+ * @see #sendRequestAsync
+ */
+ private @Nullable Object sendRequest(int command, Object argument, Integer subId,
+ long timeoutInMs) {
+ return sendRequest(command, argument, subId, null, null, timeoutInMs);
}
/**
@@ -1919,7 +1933,7 @@
* @see #sendRequestAsync
*/
private Object sendRequest(int command, Object argument, int subId, WorkSource workSource) {
- return sendRequest(command, argument, subId, null, workSource);
+ return sendRequest(command, argument, subId, null, workSource, -1 /*timeoutInMs*/);
}
/**
@@ -1928,17 +1942,18 @@
* @see #sendRequestAsync
*/
private Object sendRequest(int command, Object argument, Phone phone, WorkSource workSource) {
- return sendRequest(
- command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phone, workSource);
+ return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phone,
+ workSource, -1 /*timeoutInMs*/);
}
/**
- * Posts the specified command to be executed on the main thread,
- * waits for the request to complete, and returns the result.
+ * Posts the specified command to be executed on the main thread. If {@code timeoutInMs} is
+ * negative, waits for the request to complete, and returns the result. Otherwise, wait for
+ * maximum of {@code timeoutInMs} milliseconds, interrupt and return null.
* @see #sendRequestAsync
*/
- private Object sendRequest(
- int command, Object argument, Integer subId, Phone phone, WorkSource workSource) {
+ private @Nullable Object sendRequest(int command, Object argument, Integer subId, Phone phone,
+ WorkSource workSource, long timeoutInMs) {
if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
throw new RuntimeException("This method will deadlock if called from the main thread.");
}
@@ -1955,16 +1970,36 @@
Message msg = mMainThreadHandler.obtainMessage(command, request);
msg.sendToTarget();
- // Wait for the request to complete
+
synchronized (request) {
- while (request.result == null) {
- try {
- request.wait();
- } catch (InterruptedException e) {
- // Do nothing, go back and wait until the request is complete
+ if (timeoutInMs >= 0) {
+ // Wait for at least timeoutInMs before returning null request result
+ long now = SystemClock.elapsedRealtime();
+ long deadline = now + timeoutInMs;
+ while (request == null && now < deadline) {
+ try {
+ request.wait(deadline - now);
+ } catch (InterruptedException e) {
+ // Do nothing, go back and check if request is completed or timeout
+ } finally {
+ now = SystemClock.elapsedRealtime();
+ }
+ }
+ } else {
+ // Wait for the request to complete
+ while (request.result == null) {
+ try {
+ request.wait();
+ } catch (InterruptedException e) {
+ // Do nothing, go back and wait until the request is complete
+ }
}
}
}
+ if (request.result == null) {
+ Log.wtf(LOG_TAG,
+ "sendRequest: Blocking command timed out. Something has gone terribly wrong.");
+ }
return request.result;
}
@@ -5592,7 +5627,8 @@
return;
}
if (DBG) log("setNetworkSelectionModeAutomatic: subId " + subId);
- sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId);
+ sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId,
+ SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS);
} finally {
Binder.restoreCallingIdentity(identity);
}