Merge "IP connectivity metrics: fix LINGER/UNLINGER logging"
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 0f006b6..8686944 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -152,6 +152,9 @@
             public void onClick(View view) {
                 if (mTimePicker.validateInput()) {
                     TimePickerDialog.this.onClick(TimePickerDialog.this, BUTTON_POSITIVE);
+                    // Clearing focus forces the dialog to commit any pending
+                    // changes, e.g. typed text in a NumberPicker.
+                    mTimePicker.clearFocus();
                     dismiss();
                 }
             }
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 2281fb6..b9b9a18 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -19,6 +19,7 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 
+import java.io.PrintWriter;
 import java.util.function.Consumer;
 
 /**
@@ -399,6 +400,13 @@
         }
     }
 
+    /** @hide */
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
+        pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
+        pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
+    }
+
     private void logExcessiveCallbacks() {
         final long size = mCallbacks.size();
         final long TOO_MANY = 3000;
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 6a4fef2..4c04f78 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -27,6 +27,8 @@
  * Representation of a user on the device.
  */
 public final class UserHandle implements Parcelable {
+    // NOTE: keep logic in sync with system/core/libcutils/multiuser.c
+
     /**
      * @hide Range of uids allocated for a user.
      */
@@ -88,6 +90,19 @@
      */
     public static final boolean MU_ENABLED = true;
 
+    /** @hide */
+    public static final int ERR_GID = -1;
+    /** @hide */
+    public static final int AID_ROOT = android.os.Process.ROOT_UID;
+    /** @hide */
+    public static final int AID_APP_START = android.os.Process.FIRST_APPLICATION_UID;
+    /** @hide */
+    public static final int AID_APP_END = android.os.Process.LAST_APPLICATION_UID;
+    /** @hide */
+    public static final int AID_SHARED_GID_START = android.os.Process.FIRST_SHARED_APPLICATION_GID;
+    /** @hide */
+    public static final int AID_CACHE_GID_START = android.os.Process.FIRST_APPLICATION_CACHE_GID;
+
     final int mHandle;
 
     /**
@@ -192,13 +207,20 @@
         return getUid(userId, Process.SHARED_USER_GID);
     }
 
-    /**
-     * Returns the shared app gid for a given uid or appId.
-     * @hide
-     */
-    public static int getSharedAppGid(int id) {
-        return Process.FIRST_SHARED_APPLICATION_GID + (id % PER_USER_RANGE)
-                - Process.FIRST_APPLICATION_UID;
+    /** @hide */
+    public static int getSharedAppGid(int uid) {
+        return getSharedAppGid(getUserId(uid), getAppId(uid));
+    }
+
+    /** @hide */
+    public static int getSharedAppGid(int userId, int appId) {
+        if (appId >= AID_APP_START && appId <= AID_APP_END) {
+            return (appId - AID_APP_START) + AID_SHARED_GID_START;
+        } else if (appId >= AID_ROOT && appId <= AID_APP_START) {
+            return appId;
+        } else {
+            return -1;
+        }
     }
 
     /**
@@ -214,13 +236,18 @@
         return appId;
     }
 
-    /**
-     * Returns the cache GID for a given UID or appId.
-     * @hide
-     */
-    public static int getCacheAppGid(int id) {
-        return Process.FIRST_APPLICATION_CACHE_GID + (id % PER_USER_RANGE)
-                - Process.FIRST_APPLICATION_UID;
+    /** @hide */
+    public static int getCacheAppGid(int uid) {
+        return getCacheAppGid(getUserId(uid), getAppId(uid));
+    }
+
+    /** @hide */
+    public static int getCacheAppGid(int userId, int appId) {
+        if (appId >= AID_APP_START && appId <= AID_APP_END) {
+            return getUid(userId, (appId - AID_APP_START) + AID_CACHE_GID_START);
+        } else {
+            return -1;
+        }
     }
 
     /**
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index af26a88..21b72f3 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -530,7 +530,7 @@
      * axis distances.  Warning: this fudge factor is finely tuned, be sure to
      * run all focus tests if you dare tweak it.
      */
-    int getWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) {
+    long getWeightedDistanceFor(long majorAxisDistance, long minorAxisDistance) {
         return 13 * majorAxisDistance * majorAxisDistance
                 + minorAxisDistance * minorAxisDistance;
     }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index c123a80..15c18ac 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -51,6 +51,9 @@
 import java.util.List;
 import java.util.Objects;
 
+// TODO: use java.lang.ref.Cleaner once Android supports Java 9
+import sun.misc.Cleaner;
+
 /**
  * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
  * Autofill Framework lifecycle.
@@ -225,6 +228,9 @@
     private IAutoFillManagerClient mServiceClient;
 
     @GuardedBy("mLock")
+    private Cleaner mServiceClientCleaner;
+
+    @GuardedBy("mLock")
     private AutofillCallback mCallback;
 
     private final Context mContext;
@@ -958,10 +964,19 @@
         if (mServiceClient == null) {
             mServiceClient = new AutofillManagerClient(this);
             try {
-                final int flags = mService.addClient(mServiceClient, mContext.getUserId());
+                final int userId = mContext.getUserId();
+                final int flags = mService.addClient(mServiceClient, userId);
                 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
                 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
                 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
+                final IAutoFillManager service = mService;
+                final IAutoFillManagerClient serviceClient = mServiceClient;
+                mServiceClientCleaner = Cleaner.create(this, () -> {
+                    try {
+                        service.removeClient(serviceClient, userId);
+                    } catch (RemoteException e) {
+                    }
+                });
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -1068,6 +1083,10 @@
             if (resetClient) {
                 // Reset connection to system
                 mServiceClient = null;
+                if (mServiceClientCleaner != null) {
+                    mServiceClientCleaner.clean();
+                    mServiceClientCleaner = null;
+                }
             }
         }
     }
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 627afa7..7f2c080 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -32,6 +32,7 @@
 interface IAutoFillManager {
     // Returns flags: FLAG_ADD_CLIENT_ENABLED | FLAG_ADD_CLIENT_DEBUG | FLAG_ADD_CLIENT_VERBOSE
     int addClient(in IAutoFillManagerClient client, int userId);
+    void removeClient(in IAutoFillManagerClient client, int userId);
     int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
             in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
             String packageName);
diff --git a/core/tests/coretests/src/android/os/UserHandleTest.java b/core/tests/coretests/src/android/os/UserHandleTest.java
new file mode 100644
index 0000000..af559fd
--- /dev/null
+++ b/core/tests/coretests/src/android/os/UserHandleTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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 android.os;
+
+import static android.os.UserHandle.ERR_GID;
+import static android.os.UserHandle.getAppId;
+import static android.os.UserHandle.getCacheAppGid;
+import static android.os.UserHandle.getSharedAppGid;
+import static android.os.UserHandle.getUid;
+import static android.os.UserHandle.getUserId;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UserHandleTest {
+    // NOTE: keep logic in sync with system/core/libcutils/tests/multiuser_test.cpp
+
+    @Test
+    public void testMerge() throws Exception {
+        EXPECT_EQ(0, multiuser_get_uid(0, 0));
+        EXPECT_EQ(1000, multiuser_get_uid(0, 1000));
+        EXPECT_EQ(10000, multiuser_get_uid(0, 10000));
+        EXPECT_EQ(50000, multiuser_get_uid(0, 50000));
+        EXPECT_EQ(1000000, multiuser_get_uid(10, 0));
+        EXPECT_EQ(1001000, multiuser_get_uid(10, 1000));
+        EXPECT_EQ(1010000, multiuser_get_uid(10, 10000));
+        EXPECT_EQ(1050000, multiuser_get_uid(10, 50000));
+    }
+
+    @Test
+    public void testSplitUser() throws Exception {
+        EXPECT_EQ(0, multiuser_get_user_id(0));
+        EXPECT_EQ(0, multiuser_get_user_id(1000));
+        EXPECT_EQ(0, multiuser_get_user_id(10000));
+        EXPECT_EQ(0, multiuser_get_user_id(50000));
+        EXPECT_EQ(10, multiuser_get_user_id(1000000));
+        EXPECT_EQ(10, multiuser_get_user_id(1001000));
+        EXPECT_EQ(10, multiuser_get_user_id(1010000));
+        EXPECT_EQ(10, multiuser_get_user_id(1050000));
+    }
+
+    @Test
+    public void testSplitApp() throws Exception {
+        EXPECT_EQ(0, multiuser_get_app_id(0));
+        EXPECT_EQ(1000, multiuser_get_app_id(1000));
+        EXPECT_EQ(10000, multiuser_get_app_id(10000));
+        EXPECT_EQ(50000, multiuser_get_app_id(50000));
+        EXPECT_EQ(0, multiuser_get_app_id(1000000));
+        EXPECT_EQ(1000, multiuser_get_app_id(1001000));
+        EXPECT_EQ(10000, multiuser_get_app_id(1010000));
+        EXPECT_EQ(50000, multiuser_get_app_id(1050000));
+    }
+
+    @Test
+    public void testCache() throws Exception {
+        EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 0));
+        EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));
+        EXPECT_EQ(20000, multiuser_get_cache_gid(0, 10000));
+        EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));
+        EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 0));
+        EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 1000));
+        EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
+        EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 50000));
+    }
+
+    @Test
+    public void testShared() throws Exception {
+        EXPECT_EQ(0, multiuser_get_shared_gid(0, 0));
+        EXPECT_EQ(1000, multiuser_get_shared_gid(0, 1000));
+        EXPECT_EQ(50000, multiuser_get_shared_gid(0, 10000));
+        EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));
+        EXPECT_EQ(0, multiuser_get_shared_gid(10, 0));
+        EXPECT_EQ(1000, multiuser_get_shared_gid(10, 1000));
+        EXPECT_EQ(50000, multiuser_get_shared_gid(10, 10000));
+        EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(10, 50000));
+    }
+
+    private static void EXPECT_EQ(int expected, int actual) {
+        assertEquals(expected, actual);
+    }
+
+    private static int multiuser_get_uid(int userId, int appId) {
+        return getUid(userId, appId);
+    }
+
+    private static int multiuser_get_cache_gid(int userId, int appId) {
+        return getCacheAppGid(userId, appId);
+    }
+
+    private static int multiuser_get_shared_gid(int userId, int appId) {
+        return getSharedAppGid(userId, appId);
+    }
+
+    private static int multiuser_get_user_id(int uid) {
+        return getUserId(uid);
+    }
+
+    private static int multiuser_get_app_id(int uid) {
+        return getAppId(uid);
+    }
+}
diff --git a/legacy-test/src/com/android/internal/util/Predicate.java b/legacy-test/src/com/android/internal/util/Predicate.java
index 1b5eaff..e87f489 100644
--- a/legacy-test/src/com/android/internal/util/Predicate.java
+++ b/legacy-test/src/com/android/internal/util/Predicate.java
@@ -27,6 +27,7 @@
  * strongly encouraged to state this fact clearly in their API documentation.
  *
  * @deprecated Use {@code java.util.function.Predicate} instead.
+ *             This must not be used outside frameworks/base/test-runner.
  */
 @Deprecated
 public interface Predicate<T> {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index cb91f93..e37347a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -499,6 +499,16 @@
         }
 
         @Override
+        public void removeClient(IAutoFillManagerClient client, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    service.removeClientLocked(client);
+                }
+            }
+        }
+
+        @Override
         public void setAuthenticationResult(Bundle data, int sessionId, int authenticationId,
                 int userId) {
             synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 751c054..5c63b90 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -261,6 +261,12 @@
         return isEnabled();
     }
 
+    void removeClientLocked(IAutoFillManagerClient client) {
+        if (mClients != null) {
+            mClients.unregister(client);
+        }
+    }
+
     void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
         if (!isEnabled()) {
             return;
@@ -478,6 +484,10 @@
         }
 
         sendStateToClients(true);
+        if (mClients != null) {
+            mClients.kill();
+            mClients = null;
+        }
     }
 
     CharSequence getServiceLabel() {
@@ -605,6 +615,9 @@
             }
         }
 
+        pw.print(prefix); pw.println("Clients");
+        mClients.dump(pw, prefix2);
+
         if (mEventHistory == null || mEventHistory.getEvents() == null
                 || mEventHistory.getEvents().size() == 0) {
             pw.print(prefix); pw.println("No event on last fill response");
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 75206e4..c34c30c 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -195,6 +195,7 @@
     private LinkedList<ActiveLog> mActiveLogs;
     private LinkedList<Long> mCrashTimestamps;
     private int mCrashes;
+    private long mLastEnabledTime;
 
     // configuration from external IBinder call which is used to
     // synchronize with broadcast receiver.
@@ -2021,6 +2022,7 @@
         mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
                              quietMode ? 1 : 0, 0));
         addActiveLog(packageName, true);
+        mLastEnabledTime = SystemClock.elapsedRealtime();
     }
 
     private void addActiveLog(String packageName, boolean enable) {
@@ -2142,7 +2144,7 @@
             writer.println("  address: " + mAddress);
             writer.println("  name: " + mName);
             if (mEnable) {
-                long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime();
+                long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
                 String onDurationString = String.format("%02d:%02d:%02d.%03d",
                                           (int)(onDuration / (1000 * 60 * 60)),
                                           (int)((onDuration / (1000 * 60)) % 60),
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 90b8e29..70c08c92 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3808,6 +3808,10 @@
                 gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
                 gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
                 gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+
+                // Replace any invalid GIDs
+                if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
+                if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
             }
             checkTime(startTime, "startProcess: building args");
             if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 39c0de8..2f3c03c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -31,7 +31,6 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.util.ArraySet;
 
-import com.android.internal.util.Predicate;
 import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
 
 import org.junit.Test;
@@ -176,4 +175,16 @@
                 new File(sFilesDir.getPath() + "/snapshots/2_reduced.jpg")};
         assertTrueForFiles(existsFiles, File::exists, " must exist");
     }
+
+    /**
+     * Private predicate definition.
+     *
+     * This is needed because com.android.internal.util.Predicate is deprecated
+     * and can only be used with classes fron android.test.runner. This cannot
+     * use java.util.function.Predicate because that is not present on all API
+     * versions that this test must run on.
+     */
+    private interface Predicate<T> {
+        boolean apply(T t);
+    }
 }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 6276626..4d7c71f 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -115,10 +115,11 @@
         // for inbound parcels
         mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
 
+        // Only allow INT_MAX if unknown string mcc/mnc
         if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
             mMccStr = mccStr;
-        } else if (mccStr.isEmpty()) {
-            // If the mccStr parsed from Parcel is empty, set it as null.
+        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mccStr is empty or unknown, set it as null.
             mMccStr = null;
         } else {
             throw new IllegalArgumentException("invalid MCC format");
@@ -126,8 +127,8 @@
 
         if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
             mMncStr = mncStr;
-        } else if (mncStr.isEmpty()) {
-            // If the mncStr parsed from Parcel is empty, set it as null.
+        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mncStr is empty or unknown, set it as null.
             mMncStr = null;
         } else {
             throw new IllegalArgumentException("invalid MNC format");
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 74d2966..fd837fc 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -114,10 +114,11 @@
         mTac = tac;
         mEarfcn = earfcn;
 
+        // Only allow INT_MAX if unknown string mcc/mnc
         if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
             mMccStr = mccStr;
-        } else if (mccStr.isEmpty()) {
-            // If the mccStr parsed from Parcel is empty, set it as null.
+        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mccStr is empty or unknown, set it as null.
             mMccStr = null;
         } else {
             throw new IllegalArgumentException("invalid MCC format");
@@ -125,8 +126,8 @@
 
         if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
             mMncStr = mncStr;
-        } else if (mncStr.isEmpty()) {
-            // If the mncStr parsed from Parcel is empty, set it as null.
+        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mncStr is empty or unknown, set it as null.
             mMncStr = null;
         } else {
             throw new IllegalArgumentException("invalid MNC format");
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 51b11aa..1597245 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -114,10 +114,11 @@
         mPsc = psc;
         mUarfcn = uarfcn;
 
+        // Only allow INT_MAX if unknown string mcc/mnc
         if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
             mMccStr = mccStr;
-        } else if (mccStr.isEmpty()) {
-            // If the mccStr parsed from Parcel is empty, set it as null.
+        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mccStr is empty or unknown, set it as null.
             mMccStr = null;
         } else {
             throw new IllegalArgumentException("invalid MCC format");
@@ -125,8 +126,8 @@
 
         if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
             mMncStr = mncStr;
-        } else if (mncStr.isEmpty()) {
-            // If the mncStr parsed from Parcel is empty, set it as null.
+        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mncStr is empty or unknown, set it as null.
             mMncStr = null;
         } else {
             throw new IllegalArgumentException("invalid MNC format");
@@ -135,7 +136,7 @@
         mAlphaLong = alphal;
         mAlphaShort = alphas;
     }
-
+    
     private CellIdentityWcdma(CellIdentityWcdma cid) {
         this(cid.mLac, cid.mCid, cid.mPsc, cid.mUarfcn, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 92a21b6..7bcdcdc 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -73,8 +73,8 @@
         /**
          * Informs the user that there is some error about the scan.
          *
-         * This callback will be called whenever there is any error about the scan, but the scan
-         * won't stop unless the onComplete() callback is called.
+         * This callback will be called whenever there is any error about the scan, and the scan
+         * will be terminated. onComplete() will NOT be called.
          */
         public void onError(int error) {}
     }
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 7b07038..8e579aa 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -23,7 +23,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -46,6 +50,8 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -57,6 +63,8 @@
 public class IpSecServiceTest {
 
     private static final int DROID_SPI = 0xD1201D;
+    private static final int MAX_NUM_ENCAP_SOCKETS = 100;
+    private static final int MAX_NUM_SPIS = 100;
     private static final int TEST_UDP_ENCAP_INVALID_PORT = 100;
     private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000;
 
@@ -260,4 +268,115 @@
             }
         }
     }
+
+    /**
+     * This function checks if the number of encap UDP socket that one UID can reserve
+     * has a reasonable limit.
+     */
+    @Test
+    public void testSocketResourceTrackerLimitation() throws Exception {
+        List<IpSecUdpEncapResponse> openUdpEncapSockets = new ArrayList<IpSecUdpEncapResponse>();
+        // Reserve sockets until it fails.
+        for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) {
+            IpSecUdpEncapResponse newUdpEncapSocket =
+                    mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+            assertNotNull(newUdpEncapSocket);
+            if (IpSecManager.Status.OK != newUdpEncapSocket.status) {
+                break;
+            }
+            openUdpEncapSockets.add(newUdpEncapSocket);
+        }
+        // Assert that the total sockets quota has a reasonable limit.
+        assertTrue(
+                openUdpEncapSockets.size() > 0
+                        && openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
+
+        // Try to reserve one more UDP encapsulation socket, and should fail.
+        IpSecUdpEncapResponse extraUdpEncapSocket =
+                mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+        assertNotNull(extraUdpEncapSocket);
+        assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
+
+        // Close one of the open UDP encapsulation scokets.
+        mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
+        openUdpEncapSockets.get(0).fileDescriptor.close();
+        openUdpEncapSockets.remove(0);
+
+        // Try to reserve one more UDP encapsulation socket, and should be successful.
+        extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+        assertNotNull(extraUdpEncapSocket);
+        assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status);
+        openUdpEncapSockets.add(extraUdpEncapSocket);
+
+        // Close open UDP sockets.
+        for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) {
+            mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId);
+            openSocket.fileDescriptor.close();
+        }
+    }
+
+    /**
+     * This function checks if the number of SPI that one UID can reserve
+     * has a reasonable limit.
+     * This test does not test for both address families or duplicate SPIs because resource
+     * tracking code does not depend on them.
+     */
+    @Test
+    public void testSpiResourceTrackerLimitation() throws Exception {
+        List<IpSecSpiResponse> reservedSpis = new ArrayList<IpSecSpiResponse>();
+        // Return the same SPI for all SPI allocation since IpSecService only
+        // tracks the resource ID.
+        when(mMockNetd.ipSecAllocateSpi(
+                        anyInt(),
+                        eq(IpSecTransform.DIRECTION_OUT),
+                        anyString(),
+                        eq(InetAddress.getLoopbackAddress().getHostAddress()),
+                        anyInt()))
+                .thenReturn(DROID_SPI);
+        // Reserve spis until it fails.
+        for (int i = 0; i < MAX_NUM_SPIS; i++) {
+            IpSecSpiResponse newSpi =
+                    mIpSecService.reserveSecurityParameterIndex(
+                            0x1,
+                            InetAddress.getLoopbackAddress().getHostAddress(),
+                            DROID_SPI + i,
+                            new Binder());
+            assertNotNull(newSpi);
+            if (IpSecManager.Status.OK != newSpi.status) {
+                break;
+            }
+            reservedSpis.add(newSpi);
+        }
+        // Assert that the SPI quota has a reasonable limit.
+        assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS);
+
+        // Try to reserve one more SPI, and should fail.
+        IpSecSpiResponse extraSpi =
+                mIpSecService.reserveSecurityParameterIndex(
+                        0x1,
+                        InetAddress.getLoopbackAddress().getHostAddress(),
+                        DROID_SPI + MAX_NUM_SPIS,
+                        new Binder());
+        assertNotNull(extraSpi);
+        assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status);
+
+        // Release one reserved spi.
+        mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId);
+        reservedSpis.remove(0);
+
+        // Should successfully reserve one more spi.
+        extraSpi =
+                mIpSecService.reserveSecurityParameterIndex(
+                        0x1,
+                        InetAddress.getLoopbackAddress().getHostAddress(),
+                        DROID_SPI + MAX_NUM_SPIS,
+                        new Binder());
+        assertNotNull(extraSpi);
+        assertEquals(IpSecManager.Status.OK, extraSpi.status);
+
+        // Release reserved SPIs.
+        for (IpSecSpiResponse spiResp : reservedSpis) {
+            mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
+        }
+    }
 }