Merge "Move tests added in n-mr2 into new aosp location."
diff --git a/api/system-current.txt b/api/system-current.txt
index a2bd6c5..920780f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -40771,6 +40771,8 @@
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getIccAuthentication(int, int, java.lang.String);
+    method public java.lang.String getImei();
+    method public java.lang.String getImei(int);
     method public java.lang.String getLine1Number();
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index c7612d1..a265dd0 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -212,7 +212,7 @@
     public native final HwBlob readBuffer();
 
     public native final HwBlob readEmbeddedBuffer(
-            long parentHandle, long offset);
+            long parentHandle, long offset, boolean nullable);
 
     public native final void writeBuffer(HwBlob blob);
 
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index c5ceecd..1ee83ae 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -25,5 +25,5 @@
     boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
     boolean setupBcb(in String command);
     boolean clearBcb();
-    void rebootRecoveryWithCommand(in String command);
+    void rebootRecoveryWithCommand(in String command, in boolean update);
 }
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index d48431a..7f9ea438 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -491,15 +491,10 @@
                 command += securityArg;
             }
 
+            // RECOVERY_SERVICE writes to BCB (bootloader control block) and triggers the reboot.
             RecoverySystem rs = (RecoverySystem) context.getSystemService(
                     Context.RECOVERY_SERVICE);
-            if (!rs.setupBcb(command)) {
-                throw new IOException("Setup BCB failed");
-            }
-
-            // Having set up the BCB (bootloader control block), go ahead and reboot
-            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-            pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE);
+            rs.rebootRecoveryWithCommand(command, true /* update */);
 
             throw new IOException("Reboot failed (no permissions?)");
         }
@@ -713,7 +708,7 @@
         // Write the command into BCB (bootloader control block) and boot from
         // there. Will not return unless failed.
         RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
-        rs.rebootRecoveryWithCommand(command.toString());
+        rs.rebootRecoveryWithCommand(command.toString(), false);
 
         throw new IOException("Reboot failed (no permissions?)");
     }
@@ -913,9 +908,9 @@
      * Talks to RecoverySystemService via Binder to set up the BCB command and
      * reboot into recovery accordingly.
      */
-    private void rebootRecoveryWithCommand(String command) {
+    private void rebootRecoveryWithCommand(String command, boolean update) {
         try {
-            mService.rebootRecoveryWithCommand(command);
+            mService.rebootRecoveryWithCommand(command, update);
         } catch (RemoteException ignored) {
         }
     }
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index aefdc84..1bd2333 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -822,7 +822,8 @@
 }
 
 static jobject JHwParcel_native_readEmbeddedBuffer(
-        JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset) {
+        JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset,
+        jboolean nullable) {
     hardware::Parcel *parcel =
         JHwParcel::GetNativeContext(env, thiz)->getParcel();
 
@@ -830,11 +831,15 @@
 
     const void *ptr;
     status_t status =
-        parcel->readEmbeddedBuffer(&childHandle, parentHandle, offset, &ptr);
+        parcel->readNullableEmbeddedBuffer(&childHandle, parentHandle, offset,
+                &ptr);
 
     if (status != OK) {
         jniThrowException(env, "java/util/NoSuchElementException", NULL);
         return 0;
+    } else if (status == OK && !nullable && ptr == nullptr) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return 0;
     }
 
     return JHwBlob::NewObject(env, ptr, childHandle);
@@ -945,7 +950,7 @@
     { "readBuffer", "()L" PACKAGE_PATH "/HwBlob;",
         (void *)JHwParcel_native_readBuffer },
 
-    { "readEmbeddedBuffer", "(JJ)L" PACKAGE_PATH "/HwBlob;",
+    { "readEmbeddedBuffer", "(JJZ)L" PACKAGE_PATH "/HwBlob;",
         (void *)JHwParcel_native_readEmbeddedBuffer },
 
     { "writeBuffer", "(L" PACKAGE_PATH "/HwBlob;)V",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 66f5c4d..b3627adc 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -883,7 +883,7 @@
 
     private void handleMobileDataAlwaysOn() {
         final boolean enable = (Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 0) == 1);
+                mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1) == 1);
         final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null);
         if (enable == isEnabled) {
             return;  // Nothing to do.
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index 3c8c699..2010e64 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -181,7 +181,7 @@
         }
 
         @Override // Binder call
-        public void rebootRecoveryWithCommand(String command) {
+        public void rebootRecoveryWithCommand(String command, boolean update) {
             if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
             synchronized (sRequestLock) {
                 if (!setupOrClearBcb(true, command)) {
@@ -190,7 +190,10 @@
 
                 // Having set up the BCB, go ahead and reboot.
                 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-                pm.reboot(PowerManager.REBOOT_RECOVERY);
+                // PowerManagerService may additionally request uncrypting the package when it's
+                // to install an update (REBOOT_RECOVERY_UPDATE).
+                pm.reboot(update ? PowerManager.REBOOT_RECOVERY_UPDATE :
+                        PowerManager.REBOOT_RECOVERY);
             }
         }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index afc6247..a5876dd 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1586,9 +1586,6 @@
         public void exit() {
             // We assume that everything is reset after stopping the daemons.
             interrupt();
-            for (LocalSocket socket : mSockets) {
-                IoUtils.closeQuietly(socket);
-            }
             agentDisconnect();
             try {
                 mContext.unregisterReceiver(mBroadcastReceiver);
@@ -1601,8 +1598,26 @@
             Log.v(TAG, "Waiting");
             synchronized (TAG) {
                 Log.v(TAG, "Executing");
-                execute();
-                monitorDaemons();
+                try {
+                    execute();
+                    monitorDaemons();
+                    interrupted(); // Clear interrupt flag if execute called exit.
+                } catch (InterruptedException e) {
+                } finally {
+                    for (LocalSocket socket : mSockets) {
+                        IoUtils.closeQuietly(socket);
+                    }
+                    // This sleep is necessary for racoon to successfully complete sending delete
+                    // message to server.
+                    try {
+                        Thread.sleep(50);
+                    } catch (InterruptedException e) {
+                    }
+                    for (String daemon : mDaemons) {
+                        SystemService.stop(daemon);
+                    }
+                }
+                agentDisconnect();
             }
         }
 
@@ -1801,18 +1816,6 @@
                 Log.i(TAG, "Aborting", e);
                 updateState(DetailedState.FAILED, e.getMessage());
                 exit();
-            } finally {
-                // Kill the daemons if they fail to stop.
-                if (!initFinished) {
-                    for (String daemon : mDaemons) {
-                        SystemService.stop(daemon);
-                    }
-                }
-
-                // Do not leave an unstable state.
-                if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
-                    agentDisconnect();
-                }
             }
         }
 
@@ -1820,28 +1823,17 @@
          * Monitor the daemons we started, moving to disconnected state if the
          * underlying services fail.
          */
-        private void monitorDaemons() {
+        private void monitorDaemons() throws InterruptedException{
             if (!mNetworkInfo.isConnected()) {
                 return;
             }
-
-            try {
-                while (true) {
-                    Thread.sleep(2000);
-                    for (int i = 0; i < mDaemons.length; i++) {
-                        if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
-                            return;
-                        }
+            while (true) {
+                Thread.sleep(2000);
+                for (int i = 0; i < mDaemons.length; i++) {
+                    if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
+                        return;
                     }
                 }
-            } catch (InterruptedException e) {
-                Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
-            } finally {
-                for (String daemon : mDaemons) {
-                    SystemService.stop(daemon);
-                }
-
-                agentDisconnect();
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index eb79211..927dfd5 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -26,6 +26,7 @@
 import android.net.NetworkState;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.StateMachine;
 
 import java.util.HashMap;
@@ -72,6 +73,12 @@
         mWhat = what;
     }
 
+    @VisibleForTesting
+    public UpstreamNetworkMonitor(StateMachine tgt, int what, ConnectivityManager cm) {
+        this(null, tgt, what);
+        mCM = cm;
+    }
+
     public void start() {
         stop();
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 45d0576..6a0aa84 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -904,8 +904,10 @@
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * @hide
      */
-    /** {@hide} */
+    @SystemApi
     public String getImei() {
         return getImei(getDefaultSim());
     }
@@ -917,8 +919,10 @@
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      *
      * @param slotId of which deviceID is returned
+     *
+     * @hide
      */
-    /** {@hide} */
+    @SystemApi
     public String getImei(int slotId) {
         ITelephony telephony = getITelephony();
         if (telephony == null) return null;
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
new file mode 100644
index 0000000..b2a9a49
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.reset;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.IConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UpstreamNetworkMonitorTest {
+    private static final int EVENT_UNM_UPDATE = 1;
+
+    @Mock private Context mContext;
+    @Mock private IConnectivityManager mCS;
+
+    private TestConnectivityManager mCM;
+    private UpstreamNetworkMonitor mUNM;
+
+    @Before public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        reset(mContext);
+        reset(mCS);
+
+        mCM = new TestConnectivityManager(mContext, mCS);
+        mUNM = new UpstreamNetworkMonitor(null, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
+    }
+
+    @Test
+    public void testDoesNothingBeforeStarted() {
+        UpstreamNetworkMonitor unm = new UpstreamNetworkMonitor(null, null, EVENT_UNM_UPDATE);
+        assertFalse(unm.mobileNetworkRequested());
+        // Given a null Context, and therefore a null ConnectivityManager,
+        // these would cause an exception, if they actually attempted anything.
+        unm.mobileUpstreamRequiresDun(true);
+        unm.mobileUpstreamRequiresDun(false);
+    }
+
+    @Test
+    public void testDefaultNetworkIsTracked() throws Exception {
+        assertEquals(0, mCM.trackingDefault.size());
+
+        mUNM.start();
+        assertEquals(1, mCM.trackingDefault.size());
+
+        mUNM.stop();
+        assertTrue(mCM.isEmpty());
+    }
+
+    @Test
+    public void testListensForDunNetworks() throws Exception {
+        assertTrue(mCM.listening.isEmpty());
+
+        mUNM.start();
+        assertFalse(mCM.listening.isEmpty());
+        assertTrue(mCM.isListeningForDun());
+
+        mUNM.stop();
+        assertTrue(mCM.isEmpty());
+    }
+
+    @Test
+    public void testCanRequestMobileNetwork() throws Exception {
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.start();
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.mobileUpstreamRequiresDun(false);
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.registerMobileNetworkRequest();
+        assertTrue(mUNM.mobileNetworkRequested());
+        assertEquals(1, mCM.requested.size());
+        assertFalse(mCM.isDunRequested());
+
+        mUNM.stop();
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertTrue(mCM.isEmpty());
+    }
+
+    @Test
+    public void testCanRequestDunNetwork() throws Exception {
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.start();
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.mobileUpstreamRequiresDun(true);
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.registerMobileNetworkRequest();
+        assertTrue(mUNM.mobileNetworkRequested());
+        assertEquals(1, mCM.requested.size());
+        assertTrue(mCM.isDunRequested());
+
+        mUNM.stop();
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertTrue(mCM.isEmpty());
+    }
+
+    private static class TestConnectivityManager extends ConnectivityManager {
+        public Set<NetworkCallback> trackingDefault = new HashSet<>();
+        public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
+        public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
+
+        public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
+            super(ctx, svc);
+        }
+
+        boolean isEmpty() {
+            return trackingDefault.isEmpty() &&
+                   listening.isEmpty() &&
+                   requested.isEmpty();
+        }
+
+        boolean isListeningForDun() {
+            for (NetworkRequest req : listening.values()) {
+                if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        boolean isDunRequested() {
+            for (NetworkRequest req : requested.values()) {
+                if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
+            assertFalse(requested.containsKey(cb));
+            requested.put(cb, req);
+        }
+
+        @Override
+        public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
+            assertFalse(listening.containsKey(cb));
+            listening.put(cb, req);
+        }
+
+        @Override
+        public void registerDefaultNetworkCallback(NetworkCallback cb) {
+            assertFalse(trackingDefault.contains(cb));
+            trackingDefault.add(cb);
+        }
+
+        @Override
+        public void unregisterNetworkCallback(NetworkCallback cb) {
+            if (trackingDefault.contains(cb)) {
+                trackingDefault.remove(cb);
+            } else if (listening.containsKey(cb)) {
+                listening.remove(cb);
+            } else if (requested.containsKey(cb)) {
+                requested.remove(cb);
+            }
+
+            assertFalse(trackingDefault.contains(cb));
+            assertFalse(listening.containsKey(cb));
+            assertFalse(requested.containsKey(cb));
+        }
+    }
+}