Merge "Allow test numbers to be dialed from APM and wait for IN_SERVICE"
am: c02ca66716

Change-Id: I7627e03c55610461273f51b6220b1c0e42a98161
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index cd08289..288c72c 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -93,13 +93,12 @@
      * get an onServiceStateChanged() callback when the radio successfully comes up.
      */
     private void powerOnRadio() {
-        Log.d(this, "powerOnRadio().");
 
         // If airplane mode is on, we turn it off the same way that the Settings activity turns it
         // off.
         if (Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
-            Log.d(this, "==> Turning off airplane mode.");
+            Log.d(this, "==> Turning off airplane mode for emergency call.");
 
             // Change the system setting
             Settings.Global.putInt(mContext.getContentResolver(),
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 91a7d77..729f6a9 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -46,16 +46,16 @@
     }
 
     // Number of times to retry the call, and time between retry attempts.
+    // not final for testing
     private static int MAX_NUM_RETRIES = 5;
+    // not final for testing
     private static long TIME_BETWEEN_RETRIES_MILLIS = 5000;  // msec
 
     // Handler message codes; see handleMessage()
-    @VisibleForTesting
-    public static final int MSG_START_SEQUENCE = 1;
+    private static final int MSG_START_SEQUENCE = 1;
     @VisibleForTesting
     public static final int MSG_SERVICE_STATE_CHANGED = 2;
-    @VisibleForTesting
-    public static final int MSG_RETRY_TIMEOUT = 3;
+    private static final int MSG_RETRY_TIMEOUT = 3;
 
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index e0db44e..6d7c1f0 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -40,8 +40,8 @@
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -68,6 +68,7 @@
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Queue;
 import java.util.regex.Pattern;
 
@@ -208,26 +209,47 @@
     };
 
     // TelephonyManager Proxy interface for testing
+    @VisibleForTesting
     public interface TelephonyManagerProxy {
         int getPhoneCount();
         boolean hasIccCard(int slotId);
+        boolean isCurrentEmergencyNumber(String number);
+        Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList();
     }
 
-    private TelephonyManagerProxy mTelephonyManagerProxy = new TelephonyManagerProxy() {
-        private final TelephonyManager sTelephonyManager = TelephonyManager.getDefault();
+    private TelephonyManagerProxy mTelephonyManagerProxy;
+
+    private class TelephonyManagerProxyImpl implements TelephonyManagerProxy {
+        private final TelephonyManager mTelephonyManager;
+
+
+        TelephonyManagerProxyImpl(Context context) {
+            mTelephonyManager = new TelephonyManager(context);
+        }
 
         @Override
         public int getPhoneCount() {
-            return sTelephonyManager.getPhoneCount();
+            return mTelephonyManager.getPhoneCount();
         }
 
         @Override
         public boolean hasIccCard(int slotId) {
-            return sTelephonyManager.hasIccCard(slotId);
+            return mTelephonyManager.hasIccCard(slotId);
         }
-    };
+
+        @Override
+        public boolean isCurrentEmergencyNumber(String number) {
+            return mTelephonyManager.isCurrentEmergencyNumber(number);
+        }
+
+        @Override
+        public Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList() {
+            return mTelephonyManager.getCurrentEmergencyNumberList();
+        }
+    }
 
     //PhoneFactory proxy interface for testing
+    @VisibleForTesting
     public interface PhoneFactoryProxy {
         Phone getPhone(int index);
         Phone getDefaultPhone();
@@ -286,6 +308,7 @@
     public void onCreate() {
         super.onCreate();
         Log.initLogging(this);
+        setTelephonyManagerProxy(new TelephonyManagerProxyImpl(getApplicationContext()));
         mExpectedComponentName = new ComponentName(this, this.getClass());
         mEmergencyTonePlayer = new EmergencyTonePlayer(this);
         TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
@@ -387,9 +410,13 @@
             }
         }
 
+        final boolean isEmergencyNumber = mTelephonyManagerProxy.isCurrentEmergencyNumber(number);
+        // Find out if this is a test emergency number
+        final boolean isTestEmergencyNumber = isEmergencyNumberTestNumber(number);
+
         // Convert into emergency number if necessary
         // This is required in some regions (e.g. Taiwan).
-        if (!PhoneNumberUtils.isLocalEmergencyNumber(this, number)) {
+        if (isEmergencyNumber) {
             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false,
                     handle.getSchemeSpecificPart());
             // We only do the conversion if the phone is not in service. The un-converted
@@ -408,9 +435,6 @@
         }
         final String numberToDial = number;
 
-        final boolean isEmergencyNumber =
-                PhoneNumberUtils.isLocalEmergencyNumber(this, numberToDial);
-
 
         final boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
@@ -437,7 +461,12 @@
 
                 @Override
                 public boolean isOkToCall(Phone phone, int serviceState) {
-                    if (isEmergencyNumber) {
+                    // HAL 1.4 introduced a new variant of dial for emergency calls, which includes
+                    // an isTesting parameter. For HAL 1.4+, do not wait for IN_SERVICE, this will
+                    // be handled at the RIL/vendor level by emergencyDial(...).
+                    boolean waitForInServiceToDialEmergency = isTestEmergencyNumber
+                            && phone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
+                    if (isEmergencyNumber && !waitForInServiceToDialEmergency) {
                         // We currently only look to make sure that the radio is on before dialing.
                         // We should be able to make emergency calls at any time after the radio has
                         // been powered on and isn't in the UNAVAILABLE state, even if it is
@@ -445,9 +474,10 @@
                         return (phone.getState() == PhoneConstants.State.OFFHOOK)
                             || phone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
                     } else {
-                        // It is not an emergency number, so wait until we are in service and ready
-                        // to make calls. This can happen when we power down the radio on bluetooth
-                        // to save power on watches.
+                        // Wait until we are in service and ready to make calls. This can happen
+                        // when we power down the radio on bluetooth to save power on watches or if
+                        // it is a test emergency number and we have to wait for the device to move
+                        // IN_SERVICE before the call can take place over normal routing.
                         return (phone.getState() == PhoneConstants.State.OFFHOOK)
                             || serviceState == ServiceState.STATE_IN_SERVICE;
                     }
@@ -487,6 +517,24 @@
         }
     }
 
+    private boolean isEmergencyNumberTestNumber(String number) {
+        Map<Integer, List<EmergencyNumber>> list =
+                mTelephonyManagerProxy.getCurrentEmergencyNumberList();
+        // Do not worry about which subscription the test emergency call is on yet, only detect that
+        // it is an emergency.
+        for (Integer sub : list.keySet()) {
+            for (EmergencyNumber eNumber : list.get(sub)) {
+                if (number.equals(eNumber.getNumber())
+                        && eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST)) {
+                    Log.i(this, "isEmergencyNumberTestNumber: " + number + " has been detected as "
+                            + "a test emergency number.,");
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Whether the cellular radio is power off because the device is on Bluetooth.
      */
diff --git a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
index fb214cc..d9de2e8 100644
--- a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
+++ b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
@@ -16,17 +16,25 @@
 
 package com.android.services.telephony;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.os.AsyncResult;
 import android.os.Handler;
-import android.telephony.ServiceState;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.test.filters.FlakyTest;
+import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.ServiceStateTracker;
 
 import org.junit.After;
 import org.junit.Before;
@@ -34,16 +42,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.when;
-
 /**
  * Tests the RadioOnStateListener, which listens to one Phone and waits until its service
  * state changes to accepting emergency calls or in service. If it can not find a tower to camp onto
@@ -52,21 +50,26 @@
 @RunWith(AndroidJUnit4.class)
 public class RadioOnStateListenerTest extends TelephonyTestBase {
 
-    private static final long TIMEOUT_MS = 100;
+    private static final long TIMEOUT_MS = 1000;
 
     @Mock Phone mMockPhone;
     @Mock RadioOnStateListener.Callback mCallback;
     RadioOnStateListener mListener;
 
+    @Override
     @Before
     public void setUp() throws Exception {
         super.setUp();
         mListener = new RadioOnStateListener();
     }
 
+    @Override
     @After
     public void tearDown() throws Exception {
+        // Wait for the queue to clear...
+        waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS /*ms timeout*/);
         mListener.getHandler().removeCallbacksAndMessages(null);
+        mListener = null;
         super.tearDown();
     }
 
@@ -86,8 +89,9 @@
     }
 
     /**
-     * {@link RadioOnStateListener.Callback#isOkToCall(int)} returns true, so we are expecting
-     * {@link RadioOnStateListener.Callback#onComplete(boolean)} to return true.
+     *  {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returns true, so we are
+     *  expecting {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} to
+     *  return true.
      */
     @Test
     @SmallTest
@@ -107,8 +111,9 @@
     }
 
     /**
-     * We never receive a {@link RadioOnStateListener.Callback#onComplete(boolean)} because
-     * {@link RadioOnStateListener.Callback#isOkToCall(int)} returns false.
+     * We never receive a
+     * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} because
+     * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returns false.
      */
     @Test
     @SmallTest
@@ -129,27 +134,27 @@
     }
 
     /**
-     * Tests {@link RadioOnStateListener.Callback#isOkToCall(int)} returning false and hitting the
-     * max number of retries. This should result in
-     * {@link RadioOnStateListener.Callback#onComplete(boolean)} returning false.
+     * Tests {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returning false and
+     * hitting the max number of retries. This should result in
+     * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} returning
+     * false.
      */
     @Test
-    @FlakyTest
+    @SmallTest
     public void testTimeout_RetryFailure() {
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_POWER_OFF);
         when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
         when(mMockPhone.getServiceState()).thenReturn(state);
         when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
-        mListener.setTimeBetweenRetriesMillis(50);
+        mListener.setTimeBetweenRetriesMillis(0/*ms*/);
         mListener.setMaxNumRetries(2);
 
         // Wait for the timer to expire and check state manually in onRetryTimeout
         mListener.waitForRadioOn(mMockPhone, mCallback);
-        waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, 500);
+        waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
 
         verify(mCallback).onComplete(eq(mListener), eq(false));
         verify(mMockPhone, times(2)).setRadioPower(eq(true));
     }
-
 }