Merge "Fixed a race when slot-to-sub mapping table is rebuilding"
diff --git a/Android.mk b/Android.mk
index 915ad01..6e4e43e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -29,6 +29,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android.hardware.radio-V1.0-java \
     android.hardware.radio-V1.1-java \
+    android.hardware.radio-V1.2-java \
     android.hardware.radio.deprecated-V1.0-java \
     android.hidl.base-V1.0-java
 
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 59195f8..391de50 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -158,6 +158,17 @@
     /** New SMS received as an AsyncResult. */
     public static final int EVENT_INJECT_SMS = 8;
 
+    /** Update tracker object; used only in waiting state */
+    private static final int EVENT_UPDATE_TRACKER = 9;
+
+    /** Timeout in case state machine is stuck in a state for too long; used only in waiting
+     * state */
+    private static final int EVENT_STATE_TIMEOUT = 10;
+
+    /** Timeout duration for EVENT_STATE_TIMEOUT */
+    @VisibleForTesting
+    public static final int STATE_TIMEOUT = 30000;
+
     /** Wakelock release delay when returning to idle state. */
     private static final int WAKELOCK_TIMEOUT = 3000;
 
@@ -450,6 +461,7 @@
                     // if any broadcasts were sent, transition to waiting state
                     InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
                     if (processMessagePart(inboundSmsTracker)) {
+                        sendMessage(EVENT_UPDATE_TRACKER, inboundSmsTracker);
                         transitionTo(mWaitingState);
                     } else {
                         // if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
@@ -493,18 +505,41 @@
      * {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled.
      */
     private class WaitingState extends State {
+        private InboundSmsTracker mTracker;
+
+        @Override
+        public void enter() {
+            if (DBG) log("entering Waiting state");
+            mTracker = null;
+            sendMessageDelayed(EVENT_STATE_TIMEOUT, STATE_TIMEOUT);
+        }
+
         @Override
         public void exit() {
             if (DBG) log("exiting Waiting state");
             // Before moving to idle state, set wakelock timeout to WAKE_LOCK_TIMEOUT milliseconds
             // to give any receivers time to take their own wake locks
             setWakeLockTimeout(WAKELOCK_TIMEOUT);
+            if (VDBG) {
+                if (hasMessages(EVENT_STATE_TIMEOUT)) {
+                    log("exiting Waiting state: removing EVENT_STATE_TIMEOUT from message queue");
+                }
+                if (hasMessages(EVENT_UPDATE_TRACKER)) {
+                    log("exiting Waiting state: removing EVENT_UPDATE_TRACKER from message queue");
+                }
+            }
+            removeMessages(EVENT_STATE_TIMEOUT);
+            removeMessages(EVENT_UPDATE_TRACKER);
         }
 
         @Override
         public boolean processMessage(Message msg) {
             log("WaitingState.processMessage:" + msg.what);
             switch (msg.what) {
+                case EVENT_UPDATE_TRACKER:
+                    mTracker = (InboundSmsTracker) msg.obj;
+                    return HANDLED;
+
                 case EVENT_BROADCAST_SMS:
                     // defer until the current broadcast completes
                     deferMessage(msg);
@@ -520,6 +555,18 @@
                     // not ready to return to idle; ignore
                     return HANDLED;
 
+                case EVENT_STATE_TIMEOUT:
+                    // stuck in WaitingState for too long; drop the message and exit this state
+                    if (mTracker != null) {
+                        log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; dropping message");
+                        dropSms(new SmsBroadcastReceiver(mTracker));
+                    } else {
+                        log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; mTracker is null "
+                                + "- sending EVENT_BROADCAST_COMPLETE");
+                        sendMessage(EVENT_BROADCAST_COMPLETE);
+                    }
+                    return HANDLED;
+
                 default:
                     // parent state handles the other message types
                     return NOT_HANDLED;
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index 14c6810..46b1eef 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -125,6 +125,34 @@
                 return false;
             }
         }
+
+        if ((nsri.mRequest.searchPeriodicity < NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC)
+                || (nsri.mRequest.searchPeriodicity
+                > NetworkScanRequest.MAX_SEARCH_PERIODICITY_SEC)) {
+            return false;
+        }
+
+        if ((nsri.mRequest.maxSearchTime < NetworkScanRequest.MIN_SEARCH_MAX_SEC)
+                || (nsri.mRequest.maxSearchTime > NetworkScanRequest.MAX_SEARCH_MAX_SEC)) {
+            return false;
+        }
+
+        if ((nsri.mRequest.incrementalResultsPeriodicity
+                < NetworkScanRequest.MIN_INCREMENTAL_PERIODICITY_SEC)
+                || (nsri.mRequest.incrementalResultsPeriodicity
+                > NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC)) {
+            return false;
+        }
+
+        if ((nsri.mRequest.searchPeriodicity > nsri.mRequest.maxSearchTime)
+                || (nsri.mRequest.incrementalResultsPeriodicity > nsri.mRequest.maxSearchTime)) {
+            return false;
+        }
+
+        if ((nsri.mRequest.mccMncs != null)
+                && (nsri.mRequest.mccMncs.size() > NetworkScanRequest.MAX_MCC_MNC_LIST_SIZE)) {
+            return false;
+        }
         return true;
     }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 3ed591f..78fbd77 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -1809,56 +1809,69 @@
         }
     }
 
+    private android.hardware.radio.V1_1.RadioAccessSpecifier convertRadioAccessSpecifierToRadioHAL(
+            RadioAccessSpecifier ras) {
+        android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
+                new android.hardware.radio.V1_1.RadioAccessSpecifier();
+        rasInHalFormat.radioAccessNetwork = ras.radioAccessNetwork;
+        List<Integer> bands = null;
+        switch (ras.radioAccessNetwork) {
+            case RadioAccessNetworks.GERAN:
+                bands = rasInHalFormat.geranBands;
+                break;
+            case RadioAccessNetworks.UTRAN:
+                bands = rasInHalFormat.utranBands;
+                break;
+            case RadioAccessNetworks.EUTRAN:
+                bands = rasInHalFormat.eutranBands;
+                break;
+            default:
+                Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.radioAccessNetwork
+                        + " not supported!");
+                return null;
+        }
+
+        if (ras.bands != null) {
+            for (int band : ras.bands) {
+                bands.add(band);
+            }
+        }
+        if (ras.channels != null) {
+            for (int channel : ras.channels) {
+                rasInHalFormat.channels.add(channel);
+            }
+        }
+
+        return rasInHalFormat;
+    }
+
     @Override
     public void startNetworkScan(NetworkScanRequest nsr, Message result) {
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
-            android.hardware.radio.V1_1.IRadio radioProxy11 =
-                    android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
-            if (radioProxy11 == null) {
-                if (result != null) {
-                    AsyncResult.forMessage(result, null,
-                            CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                    result.sendToTarget();
-                }
-            } else {
-                android.hardware.radio.V1_1.NetworkScanRequest request =
-                        new android.hardware.radio.V1_1.NetworkScanRequest();
+            android.hardware.radio.V1_2.IRadio radioProxy12 =
+                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+            if (radioProxy12 != null) {
+                android.hardware.radio.V1_2.NetworkScanRequest request =
+                        new android.hardware.radio.V1_2.NetworkScanRequest();
                 request.type = nsr.scanType;
-                request.interval = 60;
+                request.interval = nsr.searchPeriodicity;
+                request.maxSearchTime = nsr.maxSearchTime;
+                request.incrementalResultsPeriodicity = nsr.incrementalResultsPeriodicity;
+                request.incrementalResults = nsr.incrementalResults;
+
                 for (RadioAccessSpecifier ras : nsr.specifiers) {
-                    android.hardware.radio.V1_1.RadioAccessSpecifier s =
-                            new android.hardware.radio.V1_1.RadioAccessSpecifier();
-                    s.radioAccessNetwork = ras.radioAccessNetwork;
-                    List<Integer> bands = null;
-                    switch (ras.radioAccessNetwork) {
-                        case RadioAccessNetworks.GERAN:
-                            bands = s.geranBands;
-                            break;
-                        case RadioAccessNetworks.UTRAN:
-                            bands = s.utranBands;
-                            break;
-                        case RadioAccessNetworks.EUTRAN:
-                            bands = s.eutranBands;
-                            break;
-                        default:
-                            Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.radioAccessNetwork
-                                    + " not supported!");
-                            return;
+
+                    android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
+                            convertRadioAccessSpecifierToRadioHAL(ras);
+                    if (rasInHalFormat == null) {
+                        return;
                     }
-                    if (ras.bands != null) {
-                        for (int band : ras.bands) {
-                            bands.add(band);
-                        }
-                    }
-                    if (ras.channels != null) {
-                        for (int channel : ras.channels) {
-                            s.channels.add(channel);
-                        }
-                    }
-                    request.specifiers.add(s);
+
+                    request.specifiers.add(rasInHalFormat);
                 }
 
+                request.mccMncs.addAll(nsr.mccMncs);
                 RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
                         mRILDefaultWorkSource);
 
@@ -1867,10 +1880,47 @@
                 }
 
                 try {
-                    radioProxy11.startNetworkScan(rr.mSerial, request);
+                    radioProxy12.startNetworkScan_1_2(rr.mSerial, request);
                 } catch (RemoteException | RuntimeException e) {
                     handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
                 }
+            } else {
+                android.hardware.radio.V1_1.IRadio radioProxy11 =
+                        android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
+                if (radioProxy11 == null) {
+                    if (result != null) {
+                        AsyncResult.forMessage(result, null,
+                                CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                        result.sendToTarget();
+                    }
+                } else {
+                    android.hardware.radio.V1_1.NetworkScanRequest request =
+                            new android.hardware.radio.V1_1.NetworkScanRequest();
+                    request.type = nsr.scanType;
+                    request.interval = nsr.searchPeriodicity;
+                    for (RadioAccessSpecifier ras : nsr.specifiers) {
+                        android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
+                                convertRadioAccessSpecifierToRadioHAL(ras);
+                        if (rasInHalFormat == null) {
+                            return;
+                        }
+
+                        request.specifiers.add(rasInHalFormat);
+                    }
+
+                    RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
+                            mRILDefaultWorkSource);
+
+                    if (RILJ_LOGD) {
+                        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+                    }
+
+                    try {
+                        radioProxy11.startNetworkScan(rr.mSerial, request);
+                    } catch (RemoteException | RuntimeException e) {
+                        handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
+                    }
+                }
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index cd7a756..802944d 100755
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -1071,6 +1071,13 @@
             }
             break;
         case NO_RESPONSE_FROM_USER:
+            // No need to send terminal response for SET UP CALL on user timeout,
+            // instead use dedicated API
+            if (type == CommandType.SET_UP_CALL) {
+                mCmdIf.handleCallSetupRequestFromSim(false, null);
+                mCurrntCmd = null;
+                return;
+            }
         case UICC_SESSION_TERM_BY_USER:
             resp = null;
             break;
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index e2904df..fa6bc3a 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -382,11 +382,8 @@
                 case EVENT_CHANGE_PIN2_DONE:
                     // a PIN/PUK/PIN2/PUK2 complete
                     // request has completed. ar.userObj is the response Message
-                    int attemptsRemaining = -1;
                     ar = (AsyncResult)msg.obj;
-                    if ((ar.exception != null) && (ar.result != null)) {
-                        attemptsRemaining = parsePinPukErrorResult(ar);
-                    }
+                    int attemptsRemaining = parsePinPukErrorResult(ar);
                     Message response = (Message)ar.userObj;
                     AsyncResult.forMessage(response).exception = ar.exception;
                     response.arg1 = attemptsRemaining;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
index fe75088..7284083 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
@@ -28,6 +28,8 @@
 
 import org.junit.Test;
 
+import java.util.ArrayList;
+
 /** Unit tests for {@link NetworkScanRequest}. */
 
 public class NetworkScanRequestTest {
@@ -44,7 +46,16 @@
         int[] lteChannels = {5, 6, 7, 8};
         RadioAccessSpecifier lte = new RadioAccessSpecifier(ranLte, lteBands, lteChannels);
         RadioAccessSpecifier[] ras = {gsm, lte};
-        NetworkScanRequest nsq = new NetworkScanRequest(NetworkScanRequest.SCAN_TYPE_ONE_SHOT, ras);
+        int searchPeriodicity = 70;
+        int maxSearchTime = 200;
+        boolean incrementalResults = true;
+        int incrementalResultsPeriodicity = 7;
+        ArrayList<String> mccmncs = new ArrayList<String>();
+        mccmncs.add("310480");
+        mccmncs.add("21002");
+        NetworkScanRequest nsq = new NetworkScanRequest(NetworkScanRequest.SCAN_TYPE_ONE_SHOT, ras,
+                  searchPeriodicity, maxSearchTime, incrementalResults,
+                  incrementalResultsPeriodicity, mccmncs);
 
         Parcel p = Parcel.obtain();
         nsq.writeToParcel(p, 0);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index e9a16bf..22070a2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -47,8 +47,8 @@
 import android.os.UserManager;
 import android.provider.Telephony;
 import android.support.test.filters.FlakyTest;
+import android.support.test.filters.MediumTest;
 import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.MediumTest;
 
 import com.android.internal.telephony.FakeSmsContentProvider;
 import com.android.internal.telephony.InboundSmsHandler;
@@ -777,4 +777,28 @@
 
         verifySmsIntentBroadcasts(0);
     }
+
+    @FlakyTest
+    @Ignore
+    @Test
+    @MediumTest
+    public void testWaitingStateTimeout() throws Exception {
+        transitionFromStartupToIdle();
+
+        // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION
+        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS,
+                new AsyncResult(null, mSmsMessage, null));
+        waitForMs(100);
+
+        ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(1)).sendBroadcast(
+                intentArgumentCaptor.capture());
+        assertEquals(Telephony.Sms.Intents.SMS_DELIVER_ACTION,
+                intentArgumentCaptor.getAllValues().get(0).getAction());
+        assertEquals("WaitingState", getCurrentState().getName());
+
+        waitForMs(InboundSmsHandler.STATE_TIMEOUT + 300);
+
+        assertEquals("IdleState", getCurrentState().getName());
+    }
 }