Merge "Rename Binder.destroy() to Binder.destroyBinder()."
diff --git a/cmds/svc/src/com/android/commands/svc/BluetoothCommand.java b/cmds/svc/src/com/android/commands/svc/BluetoothCommand.java
new file mode 100644
index 0000000..b572ce2
--- /dev/null
+++ b/cmds/svc/src/com/android/commands/svc/BluetoothCommand.java
@@ -0,0 +1,58 @@
+/*
+ * 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.commands.svc;
+
+import android.bluetooth.BluetoothAdapter;
+import android.os.RemoteException;
+
+public class BluetoothCommand extends Svc.Command {
+
+    public BluetoothCommand() {
+        super("bluetooth");
+    }
+
+    @Override
+    public String shortHelp() {
+        return "Control Bluetooth service";
+    }
+
+    @Override
+    public String longHelp() {
+        return shortHelp() + "\n"
+                + "\n"
+                + "usage: svc bluetooth [enable|disable]\n"
+                + "         Turn Bluetooth on or off.\n\n";
+    }
+
+    @Override
+    public void run(String[] args) {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+        if (adapter == null) {
+            System.err.println("Got a null BluetoothAdapter, is the system running?");
+            return;
+        }
+
+        if (args.length == 2 && "enable".equals(args[1])) {
+            adapter.enable();
+        } else if (args.length == 2 && "disable".equals(args[1])) {
+            adapter.disable();
+        } else {
+            System.err.println(longHelp());
+        }
+    }
+}
diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
index 02a92b9..020ca33 100644
--- a/cmds/svc/src/com/android/commands/svc/NfcCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
@@ -17,8 +17,6 @@
 package com.android.commands.svc;
 
 import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
 import android.nfc.INfcAdapter;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -44,42 +42,27 @@
 
     @Override
     public void run(String[] args) {
-        boolean validCommand = false;
-        if (args.length >= 2) {
-            boolean flag = false;
-            if ("enable".equals(args[1])) {
-                flag = true;
-                validCommand = true;
-            } else if ("disable".equals(args[1])) {
-                flag = false;
-                validCommand = true;
-            }
-            if (validCommand) {
-                IPackageManager pm = IPackageManager.Stub.asInterface(
-                        ServiceManager.getService("package"));
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0) ||
-			pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)) {
-                        INfcAdapter nfc = INfcAdapter.Stub
-                                .asInterface(ServiceManager.getService(Context.NFC_SERVICE));
-                        try {
-                            if (flag) {
-                                nfc.enable();
-                            } else
-                                nfc.disable(true);
-                        } catch (RemoteException e) {
-                            System.err.println("NFC operation failed: " + e);
-                        }
-                    } else {
-                        System.err.println("NFC feature not supported.");
-                    }
-                } catch (RemoteException e) {
-                    System.err.println("RemoteException while calling PackageManager, is the "
-                            + "system running?");
-                }
+        INfcAdapter adapter = INfcAdapter.Stub.asInterface(
+                ServiceManager.getService(Context.NFC_SERVICE));
+
+        if (adapter == null) {
+            System.err.println("Got a null NfcAdapter, is the system running?");
+            return;
+        }
+
+        try {
+            if (args.length == 2 && "enable".equals(args[1])) {
+                adapter.enable();
+                return;
+            } else if (args.length == 2 && "disable".equals(args[1])) {
+                adapter.disable(true);
                 return;
             }
+        } catch (RemoteException e) {
+            System.err.println("NFC operation failed: " + e);
+            return;
         }
+
         System.err.println(longHelp());
     }
 
diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java
index 2cccd1a..62225df 100644
--- a/cmds/svc/src/com/android/commands/svc/Svc.java
+++ b/cmds/svc/src/com/android/commands/svc/Svc.java
@@ -97,5 +97,6 @@
             new WifiCommand(),
             new UsbCommand(),
             new NfcCommand(),
+            new BluetoothCommand(),
     };
 }
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index 33f4e80..7d6ad21 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -174,29 +174,14 @@
     }
 
     /**
-     * Returns true if the distro IANA rules version supplied is newer or the same as the version in
-     * the system image data files.
+     * Returns true if the system image data files contain IANA rules data that are newer than the
+     * distro IANA rules version supplied, i.e. true when the version specified would be "worse"
+     * than the one that is in the system image. Returns false if the system image version is the
+     * same or older, i.e. false when the version specified would be "better" than the one that is
+     * in the system image.
      */
-    public boolean isSystemVersionOlderThan(DistroRulesVersion distroRulesVersion) {
-        return mSystemRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) < 0;
-    }
-
-    public boolean isDistroInstalled() {
-        return mDistroStatus == DISTRO_STATUS_INSTALLED;
-    }
-
-    /**
-     * Returns true if the rules version supplied is newer than the one currently installed. If
-     * there is no installed distro this method throws IllegalStateException.
-     */
-    public boolean isInstalledDistroOlderThan(DistroRulesVersion distroRulesVersion) {
-        if (mOperationInProgress) {
-            throw new IllegalStateException("Distro state not known: operation in progress.");
-        }
-        if (!isDistroInstalled()) {
-            throw new IllegalStateException("No distro installed.");
-        }
-        return mInstalledDistroRulesVersion.isOlderThan(distroRulesVersion);
+    public boolean isSystemVersionNewerThan(DistroRulesVersion distroRulesVersion) {
+        return mSystemRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) > 0;
     }
 
     public static final Parcelable.Creator<RulesState> CREATOR =
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ff52f27..838fb72 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -70,9 +70,10 @@
  * devices, and start a scan for Bluetooth LE devices.
  *
  * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth
- * adapter, when running on JELLY_BEAN_MR1 and below, call the
- * static {@link #getDefaultAdapter} method; when running on JELLY_BEAN_MR2 and
- * higher, call {@link BluetoothManager#getAdapter}.
+ * adapter, call the {@link BluetoothManager#getAdapter} function on {@link BluetoothManager}.
+ * On JELLY_BEAN_MR1 and below you will need to use the static {@link #getDefaultAdapter}
+ * method instead.
+ * </p><p>
  * Fundamentally, this is your starting point for all
  * Bluetooth actions. Once you have the local adapter, you can get a set of
  * {@link BluetoothDevice} objects representing all paired devices with
@@ -81,14 +82,13 @@
  * listen for incoming connection requests with
  * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}; or start a scan for
  * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
- *
- * <p>This class is thread safe.
- *
+ * </p>
+ * <p>This class is thread safe.</p>
  * <p class="note"><strong>Note:</strong>
  * Most methods require the {@link android.Manifest.permission#BLUETOOTH}
  * permission and some also require the
  * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
+ * </p>
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>
@@ -565,6 +565,7 @@
      * <p>Currently Android only supports one Bluetooth adapter, but the API
      * could be extended to support more. This will always return the default
      * adapter.
+     * </p>
      * @return the default local adapter, or null if Bluetooth is not supported
      *         on this hardware platform
      */
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 29283e7..4c21aae 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -32,10 +32,7 @@
  * Use {@link android.content.Context#getSystemService(java.lang.String)}
  * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager},
  * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}.
- * <p>
- * Alternately, you can just call the static helper
- * {@link BluetoothAdapter#getDefaultAdapter()}.
- *
+ * </p>
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 67d56d5..dfd5996 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -208,6 +208,8 @@
             if (wrapper == null) return;
 
             stopAdvertisingSet(wrapper);
+
+            mLegacyAdvertisers.remove(callback);
         }
     }
 
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9e8acd0..02f0f18 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -596,7 +596,16 @@
     /**
      * @hide
      */
-    public final static int REQUEST_ID_UNSET = 0;
+    public static final int REQUEST_ID_UNSET = 0;
+
+    /**
+     * Static unique request used as a tombstone for NetworkCallbacks that have been unregistered.
+     * This allows to distinguish when unregistering NetworkCallbacks those that were never
+     * registered and those that were already unregistered.
+     * @hide
+     */
+    private static final NetworkRequest ALREADY_UNREGISTERED =
+            new NetworkRequest.Builder().clearCapabilities().build();
 
     /**
      * A NetID indicating no Network is selected.
@@ -2691,10 +2700,6 @@
         public void onNetworkResumed(Network network) {}
 
         private NetworkRequest networkRequest;
-
-        private boolean isRegistered() {
-            return (networkRequest != null) && (networkRequest.requestId != REQUEST_ID_UNSET);
-        }
     }
 
     /**
@@ -2861,7 +2866,8 @@
         final NetworkRequest request;
         try {
             synchronized(sCallbacks) {
-                if (callback.isRegistered()) {
+                if (callback.networkRequest != null
+                        && callback.networkRequest != ALREADY_UNREGISTERED) {
                     // TODO: throw exception instead and enforce 1:1 mapping of callbacks
                     // and requests (http://b/20701525).
                     Log.e(TAG, "NetworkCallback was already registered");
@@ -3312,8 +3318,10 @@
         // Find all requests associated to this callback and stop callback triggers immediately.
         // Callback is reusable immediately. http://b/20701525, http://b/35921499.
         synchronized (sCallbacks) {
-            Preconditions.checkArgument(
-                    networkCallback.isRegistered(), "NetworkCallback was not registered");
+            Preconditions.checkArgument(networkCallback.networkRequest != null,
+                    "NetworkCallback was not registered");
+            Preconditions.checkArgument(networkCallback.networkRequest != ALREADY_UNREGISTERED,
+                    "NetworkCallback was already unregistered");
             for (Map.Entry<NetworkRequest, NetworkCallback> e : sCallbacks.entrySet()) {
                 if (e.getValue() == networkCallback) {
                     reqs.add(e.getKey());
@@ -3329,7 +3337,7 @@
                 // Only remove mapping if rpc was successful.
                 sCallbacks.remove(r);
             }
-            networkCallback.networkRequest = null;
+            networkCallback.networkRequest = ALREADY_UNREGISTERED;
         }
     }
 
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index c34de15..92e78bc 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -97,6 +97,12 @@
     void enableIpv6(String iface);
 
     /**
+     * Set IPv6 autoconf address generation mode.
+     * This is a no-op if an unsupported mode is requested.
+     */
+    void setIPv6AddrGenMode(String iface, int mode);
+
+    /**
      * Enables or enables IPv6 ND offload.
      */
     void setInterfaceIpv6NdOffload(String iface, boolean enable);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index d6688e3..d5e240a 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -740,7 +740,8 @@
     /**
      * Return the current priority of a thread, based on Linux priorities.
      * 
-     * @param tid The identifier of the thread/process to change.
+     * @param tid The identifier of the thread/process. If tid equals zero, the priority of the
+     * calling process/thread will be returned.
      * 
      * @return Returns the current priority, as a Linux priority level,
      * from -20 for highest scheduling priority to 19 for lowest scheduling
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index aced75d..1a556d5 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -160,6 +160,8 @@
             argv = removedArgs;
         }
 
+        // Perform the same initialization that would happen after the Zygote forks.
+        Zygote.nativePreApplicationInit();
         RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
     }
 
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e065843..91d9d1e 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -121,6 +121,11 @@
           int[] fdsToIgnore, String instructionSet, String appDataDir);
 
     /**
+     * Called to do any initialization before starting an application.
+     */
+    native static void nativePreApplicationInit();
+
+    /**
      * Special method to start the system server process. In addition to the
      * common actions performed in forkAndSpecialize, the pid of the child
      * process is recorded such that the death of the child process will cause
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index b5c8815..a271aad 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -58,6 +58,29 @@
     jmethodID onTransactID;
 } gFields;
 
+struct JHwBinderHolder : public RefBase {
+    JHwBinderHolder() {}
+
+    sp<JHwBinder> get(JNIEnv *env, jobject obj) {
+        Mutex::Autolock autoLock(mLock);
+
+        sp<JHwBinder> binder = mBinder.promote();
+
+        if (binder == NULL) {
+            binder = new JHwBinder(env, obj);
+            mBinder = binder;
+        }
+
+        return binder;
+    }
+
+private:
+    Mutex mLock;
+    wp<JHwBinder> mBinder;
+
+    DISALLOW_COPY_AND_ASSIGN(JHwBinderHolder);
+};
+
 // static
 void JHwBinder::InitClass(JNIEnv *env) {
     ScopedLocalRef<jclass> clazz(
@@ -75,10 +98,10 @@
 }
 
 // static
-sp<JHwBinder> JHwBinder::SetNativeContext(
-        JNIEnv *env, jobject thiz, const sp<JHwBinder> &context) {
-    sp<JHwBinder> old =
-        (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+sp<JHwBinderHolder> JHwBinder::SetNativeContext(
+        JNIEnv *env, jobject thiz, const sp<JHwBinderHolder> &context) {
+    sp<JHwBinderHolder> old =
+        (JHwBinderHolder *)env->GetLongField(thiz, gFields.contextID);
 
     if (context != NULL) {
         context->incStrong(NULL /* id */);
@@ -94,27 +117,27 @@
 }
 
 // static
-sp<JHwBinder> JHwBinder::GetNativeContext(
+sp<JHwBinder> JHwBinder::GetNativeBinder(
         JNIEnv *env, jobject thiz) {
-    return (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+    JHwBinderHolder *holder =
+        reinterpret_cast<JHwBinderHolder *>(
+                env->GetLongField(thiz, gFields.contextID));
+
+    return holder->get(env, thiz);
 }
 
 JHwBinder::JHwBinder(JNIEnv *env, jobject thiz) {
     jclass clazz = env->GetObjectClass(thiz);
     CHECK(clazz != NULL);
 
-    mClass = (jclass)env->NewGlobalRef(clazz);
-    mObject = env->NewWeakGlobalRef(thiz);
+    mObject = env->NewGlobalRef(thiz);
 }
 
 JHwBinder::~JHwBinder() {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
 
-    env->DeleteWeakGlobalRef(mObject);
+    env->DeleteGlobalRef(mObject);
     mObject = NULL;
-
-    env->DeleteGlobalRef(mClass);
-    mClass = NULL;
 }
 
 status_t JHwBinder::onTransact(
@@ -201,10 +224,10 @@
 using namespace android;
 
 static void releaseNativeContext(void *nativeContext) {
-    sp<JHwBinder> binder = (JHwBinder *)nativeContext;
+    sp<JHwBinderHolder> context = static_cast<JHwBinderHolder *>(nativeContext);
 
-    if (binder != NULL) {
-        binder->decStrong(NULL /* id */);
+    if (context != NULL) {
+        context->decStrong(NULL /* id */);
     }
 }
 
@@ -215,8 +238,7 @@
 }
 
 static void JHwBinder_native_setup(JNIEnv *env, jobject thiz) {
-    sp<JHwBinder> context = new JHwBinder(env, thiz);
-
+    sp<JHwBinderHolder> context = new JHwBinderHolder;
     JHwBinder::SetNativeContext(env, thiz, context);
 }
 
@@ -244,7 +266,7 @@
         return;  // XXX exception already pending?
     }
 
-    sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
+    sp<hardware::IBinder> binder = JHwBinder::GetNativeBinder(env, thiz);
 
     /* TODO(b/33440494) this is not right */
     sp<hidl::base::V1_0::IBase> base = new hidl::base::V1_0::BpHwBase(binder);
diff --git a/core/jni/android_os_HwBinder.h b/core/jni/android_os_HwBinder.h
index fa8fe01..5352f1e 100644
--- a/core/jni/android_os_HwBinder.h
+++ b/core/jni/android_os_HwBinder.h
@@ -24,13 +24,15 @@
 
 namespace android {
 
+struct JHwBinderHolder;
+
 struct JHwBinder : public hardware::BHwBinder {
     static void InitClass(JNIEnv *env);
 
-    static sp<JHwBinder> SetNativeContext(
-            JNIEnv *env, jobject thiz, const sp<JHwBinder> &context);
+    static sp<JHwBinderHolder> SetNativeContext(
+            JNIEnv *env, jobject thiz, const sp<JHwBinderHolder> &context);
 
-    static sp<JHwBinder> GetNativeContext(JNIEnv *env, jobject thiz);
+    static sp<JHwBinder> GetNativeBinder(JNIEnv *env, jobject thiz);
 
     JHwBinder(JNIEnv *env, jobject thiz);
 
@@ -45,7 +47,6 @@
             TransactCallback callback);
 
 private:
-    jclass mClass;
     jobject mObject;
 
     DISALLOW_COPY_AND_ASSIGN(JHwBinder);
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index b21ea828..6ea809a 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -169,7 +169,6 @@
     jclass clazz = env->GetObjectClass(thiz);
     CHECK(clazz != NULL);
 
-    mClass = (jclass)env->NewGlobalRef(clazz);
     mObject = env->NewWeakGlobalRef(thiz);
 }
 
@@ -182,9 +181,6 @@
 
     env->DeleteWeakGlobalRef(mObject);
     mObject = NULL;
-
-    env->DeleteGlobalRef(mClass);
-    mClass = NULL;
 }
 
 hardware::Parcel *JHwParcel::getParcel() {
@@ -542,7 +538,7 @@
                 env, FindClassOrDie(env, PACKAGE_PATH "/HwRemoteBinder"));
 
         if (env->IsInstanceOf(binderObj, hwBinderKlass.get())) {
-            binder = JHwBinder::GetNativeContext(env, binderObj);
+            binder = JHwBinder::GetNativeBinder(env, binderObj);
         } else if (env->IsInstanceOf(binderObj, hwRemoteBinderKlass.get())) {
             binder = JHwRemoteBinder::GetNativeContext(
                     env, binderObj)->getBinder();
diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h
index f81de9b..f6e6100 100644
--- a/core/jni/android_os_HwParcel.h
+++ b/core/jni/android_os_HwParcel.h
@@ -53,7 +53,6 @@
     virtual ~JHwParcel();
 
 private:
-    jclass mClass;
     jobject mObject;
 
     hardware::Parcel *mParcel;
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index f2f8e52..9c2ee9c 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -272,7 +272,6 @@
     jclass clazz = env->GetObjectClass(thiz);
     CHECK(clazz != NULL);
 
-    mClass = (jclass)env->NewGlobalRef(clazz);
     mObject = env->NewWeakGlobalRef(thiz);
 }
 
@@ -281,9 +280,6 @@
 
     env->DeleteWeakGlobalRef(mObject);
     mObject = NULL;
-
-    env->DeleteGlobalRef(mClass);
-    mClass = NULL;
 }
 
 sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index 77a0278..63aad0a 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -68,7 +68,6 @@
     virtual ~JHwRemoteBinder();
 
 private:
-    jclass mClass;
     jobject mObject;
 
     sp<hardware::IBinder> mBinder;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index d73e7dd..cb53106 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -221,6 +221,14 @@
 // The debug malloc library needs to know whether it's the zygote or a child.
 extern "C" int gMallocLeakZygoteChild;
 
+static void PreApplicationInit() {
+  // The child process sets this to indicate it's not the zygote.
+  gMallocLeakZygoteChild = 1;
+
+  // Set the jemalloc decay time to 1.
+  mallopt(M_DECAY_TIME, 1);
+}
+
 static void EnableKeepCapabilities(JNIEnv* env) {
   int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
   if (rc == -1) {
@@ -517,11 +525,7 @@
   pid_t pid = fork();
 
   if (pid == 0) {
-    // The child process.
-    gMallocLeakZygoteChild = 1;
-
-    // Set the jemalloc decay time to 1.
-    mallopt(M_DECAY_TIME, 1);
+    PreApplicationInit();
 
     // Clean up any descriptors which must be closed immediately
     DetachDescriptors(env, fdsToClose);
@@ -678,6 +682,10 @@
 
 namespace android {
 
+static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) {
+  PreApplicationInit();
+}
+
 static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
         JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
         jint debug_flags, jobjectArray rlimits,
@@ -807,7 +815,9 @@
     { "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
       (void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork },
     { "nativeUnmountStorageOnInit", "()V",
-      (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
+      (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit },
+    { "nativePreApplicationInit", "()V",
+      (void *) com_android_internal_os_Zygote_nativePreApplicationInit }
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/res/res/values-mcc204-mnc12/config.xml b/core/res/res/values-mcc204-mnc12/config.xml
deleted file mode 100644
index 80432d7..0000000
--- a/core/res/res/values-mcc204-mnc12/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2015, 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 my 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>20408</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc206-mnc05/config.xml b/core/res/res/values-mcc206-mnc05/config.xml
deleted file mode 100644
index a684aaa..0000000
--- a/core/res/res/values-mcc206-mnc05/config.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>20610</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc206-mnc10/config.xml b/core/res/res/values-mcc206-mnc10/config.xml
deleted file mode 100644
index 5c96317..0000000
--- a/core/res/res/values-mcc206-mnc10/config.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>20605</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc208-mnc15/config.xml b/core/res/res/values-mcc208-mnc15/config.xml
deleted file mode 100644
index 32b951c..0000000
--- a/core/res/res/values-mcc208-mnc15/config.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>20801</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc208-mnc26/config.xml b/core/res/res/values-mcc208-mnc26/config.xml
deleted file mode 100644
index 31d2d0f..0000000
--- a/core/res/res/values-mcc208-mnc26/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>20801</item>
-        <item>20810</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc02/config.xml b/core/res/res/values-mcc214-mnc02/config.xml
deleted file mode 100755
index c83de57..0000000
--- a/core/res/res/values-mcc214-mnc02/config.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mcc214-mnc04/config.xml b/core/res/res/values-mcc214-mnc04/config.xml
deleted file mode 100644
index 6dfa87b..0000000
--- a/core/res/res/values-mcc214-mnc04/config.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc05/config.xml b/core/res/res/values-mcc214-mnc05/config.xml
deleted file mode 100755
index 9302b0c..0000000
--- a/core/res/res/values-mcc214-mnc05/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc06/config.xml b/core/res/res/values-mcc214-mnc06/config.xml
deleted file mode 100755
index c3f2643..0000000
--- a/core/res/res/values-mcc214-mnc06/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc08/config.xml b/core/res/res/values-mcc214-mnc08/config.xml
deleted file mode 100755
index 5af6d5d..0000000
--- a/core/res/res/values-mcc214-mnc08/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc09/config.xml b/core/res/res/values-mcc214-mnc09/config.xml
deleted file mode 100755
index d789771..0000000
--- a/core/res/res/values-mcc214-mnc09/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc10/config.xml b/core/res/res/values-mcc214-mnc10/config.xml
deleted file mode 100755
index b66e1a2..0000000
--- a/core/res/res/values-mcc214-mnc10/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc11/config.xml b/core/res/res/values-mcc214-mnc11/config.xml
deleted file mode 100755
index 9fd06db..0000000
--- a/core/res/res/values-mcc214-mnc11/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc12/config.xml b/core/res/res/values-mcc214-mnc12/config.xml
deleted file mode 100755
index 7468238..0000000
--- a/core/res/res/values-mcc214-mnc12/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc13/config.xml b/core/res/res/values-mcc214-mnc13/config.xml
deleted file mode 100755
index 35ff4ae..0000000
--- a/core/res/res/values-mcc214-mnc13/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc14/config.xml b/core/res/res/values-mcc214-mnc14/config.xml
deleted file mode 100755
index b6a7440..0000000
--- a/core/res/res/values-mcc214-mnc14/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc15/config.xml b/core/res/res/values-mcc214-mnc15/config.xml
deleted file mode 100755
index 8296410..0000000
--- a/core/res/res/values-mcc214-mnc15/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc16/config.xml b/core/res/res/values-mcc214-mnc16/config.xml
deleted file mode 100755
index 1aaf577..0000000
--- a/core/res/res/values-mcc214-mnc16/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc17/config.xml b/core/res/res/values-mcc214-mnc17/config.xml
deleted file mode 100755
index be92a32..0000000
--- a/core/res/res/values-mcc214-mnc17/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc18/config.xml b/core/res/res/values-mcc214-mnc18/config.xml
deleted file mode 100755
index 078d7e2..0000000
--- a/core/res/res/values-mcc214-mnc18/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc19/config.xml b/core/res/res/values-mcc214-mnc19/config.xml
deleted file mode 100755
index d194687..0000000
--- a/core/res/res/values-mcc214-mnc19/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc20/config.xml b/core/res/res/values-mcc214-mnc20/config.xml
deleted file mode 100755
index 6aaf970..0000000
--- a/core/res/res/values-mcc214-mnc20/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21421</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc214-mnc21/config.xml b/core/res/res/values-mcc214-mnc21/config.xml
deleted file mode 100755
index f890b14..0000000
--- a/core/res/res/values-mcc214-mnc21/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21401</item>
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc219-mnc02/config.xml b/core/res/res/values-mcc219-mnc02/config.xml
deleted file mode 100644
index 2ac6ba6..0000000
--- a/core/res/res/values-mcc219-mnc02/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2015, 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 my 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21901</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc232-mnc10/config.xml b/core/res/res/values-mcc232-mnc10/config.xml
deleted file mode 100644
index bdf83016..0000000
--- a/core/res/res/values-mcc232-mnc10/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- ** Copyright 2016, 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.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23203</item>
-        <item>23205</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc232-mnc11/config.xml b/core/res/res/values-mcc232-mnc11/config.xml
deleted file mode 100644
index 91e37b4..0000000
--- a/core/res/res/values-mcc232-mnc11/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- ** Copyright 2016, 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.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23201</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc232-mnc12/config.xml b/core/res/res/values-mcc232-mnc12/config.xml
deleted file mode 100644
index 91e37b4..0000000
--- a/core/res/res/values-mcc232-mnc12/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- ** Copyright 2016, 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.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23201</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc232-mnc13/config.xml b/core/res/res/values-mcc232-mnc13/config.xml
deleted file mode 100644
index 2c14f87..0000000
--- a/core/res/res/values-mcc232-mnc13/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- ** Copyright 2016, 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.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23203</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc08/config.xml b/core/res/res/values-mcc234-mnc08/config.xml
deleted file mode 100644
index 13d4d8f..0000000
--- a/core/res/res/values-mcc234-mnc08/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2014, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23430</item>
-        <item>23433</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc10/config.xml b/core/res/res/values-mcc234-mnc10/config.xml
deleted file mode 100644
index b704d3f..0000000
--- a/core/res/res/values-mcc234-mnc10/config.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23420</item>
-        <item>23426</item>
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc20/config.xml b/core/res/res/values-mcc234-mnc20/config.xml
index 27c91d2..1e4bb0b 100644
--- a/core/res/res/values-mcc234-mnc20/config.xml
+++ b/core/res/res/values-mcc234-mnc20/config.xml
@@ -32,15 +32,4 @@
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1440</integer>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23410</item>
-        <item>23426</item>
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mcc234-mnc26/config.xml b/core/res/res/values-mcc234-mnc26/config.xml
deleted file mode 100644
index 8d259de..0000000
--- a/core/res/res/values-mcc234-mnc26/config.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23410</item>
-        <item>23420</item>
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc30/config.xml b/core/res/res/values-mcc234-mnc30/config.xml
deleted file mode 100644
index eabdf9a..0000000
--- a/core/res/res/values-mcc234-mnc30/config.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc31/config.xml b/core/res/res/values-mcc234-mnc31/config.xml
deleted file mode 100644
index eabdf9a..0000000
--- a/core/res/res/values-mcc234-mnc31/config.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc32/config.xml b/core/res/res/values-mcc234-mnc32/config.xml
deleted file mode 100644
index eabdf9a..0000000
--- a/core/res/res/values-mcc234-mnc32/config.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc33/config.xml b/core/res/res/values-mcc234-mnc33/config.xml
deleted file mode 100644
index 776b570..0000000
--- a/core/res/res/values-mcc234-mnc33/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds.  Do not translate. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc34/config.xml b/core/res/res/values-mcc234-mnc34/config.xml
deleted file mode 100644
index eabdf9a..0000000
--- a/core/res/res/values-mcc234-mnc34/config.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc234-mnc86/config.xml b/core/res/res/values-mcc234-mnc86/config.xml
deleted file mode 100644
index eabdf9a..0000000
--- a/core/res/res/values-mcc234-mnc86/config.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>23430</item>
-        <item>23431</item>
-        <item>23432</item>
-        <item>23433</item>
-        <item>23434</item>
-        <item>23486</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml
index 05265c7..4876a8ae 100644
--- a/core/res/res/values-mcc302-mnc370/config.xml
+++ b/core/res/res/values-mcc302-mnc370/config.xml
@@ -35,14 +35,6 @@
     -->
     <integer name="config_mobile_mtu">1410</integer>
 
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>302220</item>
-        <item>302610</item>
-        <item>302660</item>
-        <item>302720</item>
-        <item>302780</item>
-    </string-array>
-
   <!-- Values for GPS configuration (Rogers) -->
     <string-array translatable="false" name="config_gpsParameters">
         <item>SUPL_HOST=supl.google.com</item>
diff --git a/core/res/res/values-mcc302-mnc500/config.xml b/core/res/res/values-mcc302-mnc500/config.xml
deleted file mode 100644
index 77f6419..0000000
--- a/core/res/res/values-mcc302-mnc500/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- ** Copyright 2016, 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.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>302</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc510/config.xml b/core/res/res/values-mcc302-mnc510/config.xml
deleted file mode 100644
index 77f6419..0000000
--- a/core/res/res/values-mcc302-mnc510/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- ** Copyright 2016, 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.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>302</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc610/config.xml b/core/res/res/values-mcc302-mnc610/config.xml
index 0af2c39..8ac8f4c 100644
--- a/core/res/res/values-mcc302-mnc610/config.xml
+++ b/core/res/res/values-mcc302-mnc610/config.xml
@@ -18,11 +18,6 @@
 -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>302</item>
-    </string-array>
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1428</integer>
diff --git a/core/res/res/values-mcc302-mnc640/config.xml b/core/res/res/values-mcc302-mnc640/config.xml
index e005bc0..cba8eed 100644
--- a/core/res/res/values-mcc302-mnc640/config.xml
+++ b/core/res/res/values-mcc302-mnc640/config.xml
@@ -18,11 +18,6 @@
 -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>302</item>
-    </string-array>
-
     <!-- Values for GPS configuration (Bell) -->
     <string-array translatable="false" name="config_gpsParameters">
         <item>SUPL_HOST=supl.google.com</item>
diff --git a/core/res/res/values-mcc302-mnc660/config.xml b/core/res/res/values-mcc302-mnc660/config.xml
index c689d22..8c2e702 100644
--- a/core/res/res/values-mcc302-mnc660/config.xml
+++ b/core/res/res/values-mcc302-mnc660/config.xml
@@ -43,12 +43,4 @@
     -->
     <integer name="config_mobile_mtu">1430</integer>
 
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>302220</item>
-        <item>302370</item>
-        <item>302610</item>
-        <item>302720</item>
-        <item>302780</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml
index 7a3540a..dff3678 100644
--- a/core/res/res/values-mcc302-mnc720/config.xml
+++ b/core/res/res/values-mcc302-mnc720/config.xml
@@ -37,14 +37,6 @@
     -->
     <integer name="config_mobile_mtu">1430</integer>
 
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>302220</item>
-        <item>302370</item>
-        <item>302610</item>
-        <item>302660</item>
-        <item>302780</item>
-    </string-array>
-
   <!-- Values for GPS configuration (Rogers) -->
     <string-array translatable="false" name="config_gpsParameters">
         <item>SUPL_HOST=supl.google.com</item>
diff --git a/core/res/res/values-mcc302-mnc780/config.xml b/core/res/res/values-mcc302-mnc780/config.xml
index a48f695..d300dab 100644
--- a/core/res/res/values-mcc302-mnc780/config.xml
+++ b/core/res/res/values-mcc302-mnc780/config.xml
@@ -20,12 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>302</item>
-    </string-array>
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1358</integer>
diff --git a/core/res/res/values-mcc310-mnc150/config.xml b/core/res/res/values-mcc310-mnc150/config.xml
index e1f696e..e7d1325 100644
--- a/core/res/res/values-mcc310-mnc150/config.xml
+++ b/core/res/res/values-mcc310-mnc150/config.xml
@@ -18,23 +18,6 @@
 -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Show roaming icon though same named operators. -->
-    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
-        <item>310110</item>
-        <item>310140</item>
-        <item>310400</item>
-        <item>310470</item>
-        <item>311170</item>
-    </string-array>
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>310</item>
-        <item>311</item>
-        <item>312</item>
-        <item>313</item>
-        <item>314</item>
-        <item>315</item>
-        <item>316</item>
-    </string-array>
     <string-array translatable="false" name="config_twoDigitNumberPattern">
         <item>"0"</item>
         <item>"00"</item>
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index 9accdf0..00ab712 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -20,28 +20,9 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1410</integer>
-    <!-- Show roaming icon though same named operators. -->
-    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
-        <item>310110</item>
-        <item>310140</item>
-        <item>310400</item>
-        <item>310470</item>
-        <item>311170</item>
-    </string-array>
-
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>310</item>
-        <item>311</item>
-        <item>312</item>
-        <item>313</item>
-        <item>314</item>
-        <item>315</item>
-        <item>316</item>
-    </string-array>
     <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
     <string-array name="config_twoDigitNumberPattern">
         <item>"0"</item>
diff --git a/core/res/res/values-mcc340-mnc01/config.xml b/core/res/res/values-mcc340-mnc01/config.xml
index cfc1380..1ca8963 100644
--- a/core/res/res/values-mcc340-mnc01/config.xml
+++ b/core/res/res/values-mcc340-mnc01/config.xml
@@ -38,10 +38,4 @@
     <string-array translatable="false" name="config_tether_apndata">
         <item>Orangeweb,orangeweb,,,orange,orange,,,,,340,01,1,DUN</item>
     </string-array>
-
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>20801</item>
-        <item>20815</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mcc404/config.xml b/core/res/res/values-mcc404/config.xml
index 17539d8..6b77e9c 100644
--- a/core/res/res/values-mcc404/config.xml
+++ b/core/res/res/values-mcc404/config.xml
@@ -18,11 +18,6 @@
 -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Show roaming icon though same named operators. -->
-    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
-        <item>404</item>
-        <item>405</item>
-    </string-array>
     <!-- Whether camera shutter sound is forced or not  (country specific). -->
     <bool name="config_camera_sound_forced">true</bool>
 </resources>
diff --git a/core/res/res/values-mcc405/config.xml b/core/res/res/values-mcc405/config.xml
index 17539d8..6b77e9c 100644
--- a/core/res/res/values-mcc405/config.xml
+++ b/core/res/res/values-mcc405/config.xml
@@ -18,11 +18,6 @@
 -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Show roaming icon though same named operators. -->
-    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
-        <item>404</item>
-        <item>405</item>
-    </string-array>
     <!-- Whether camera shutter sound is forced or not  (country specific). -->
     <bool name="config_camera_sound_forced">true</bool>
 </resources>
diff --git a/core/res/res/values-mcc425-mnc07/config.xml b/core/res/res/values-mcc425-mnc07/config.xml
index a092fb9..770cebd 100644
--- a/core/res/res/values-mcc425-mnc07/config.xml
+++ b/core/res/res/values-mcc425-mnc07/config.xml
@@ -39,9 +39,4 @@
     <string-array translatable="false" name="config_tether_apndata">
       <item>PC HOT mobile,pc.hotm,,,,,,,,,425,07,,DUN</item>
     </string-array>
-
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>42503</item>
-    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc425-mnc08/config.xml b/core/res/res/values-mcc425-mnc08/config.xml
deleted file mode 100644
index 8470b86..0000000
--- a/core/res/res/values-mcc425-mnc08/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>42502</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc510-mnc21/config.xml b/core/res/res/values-mcc510-mnc21/config.xml
deleted file mode 100644
index 1fd9dfa..0000000
--- a/core/res/res/values-mcc510-mnc21/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>51001</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc520/config.xml b/core/res/res/values-mcc520/config.xml
deleted file mode 100644
index b2f3efa..0000000
--- a/core/res/res/values-mcc520/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Show roaming icon though same named operators. -->
-    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
-        <item>520</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc530-mnc24/config.xml b/core/res/res/values-mcc530-mnc24/config.xml
deleted file mode 100644
index 5598e8d..0000000
--- a/core/res/res/values-mcc530-mnc24/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Show roaming icon though same named operators. -->
-    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
-        <item>53001</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc722-mnc36/config.xml b/core/res/res/values-mcc722-mnc36/config.xml
deleted file mode 100644
index daf5373..0000000
--- a/core/res/res/values-mcc722-mnc36/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2016, 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 my 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>72234</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc730-mnc01/config.xml b/core/res/res/values-mcc730-mnc01/config.xml
deleted file mode 100644
index 22f4027..0000000
--- a/core/res/res/values-mcc730-mnc01/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2015, 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 my 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>73010</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc730-mnc07/config.xml b/core/res/res/values-mcc730-mnc07/config.xml
deleted file mode 100644
index 836ddf9..0000000
--- a/core/res/res/values-mcc730-mnc07/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2015, 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 my 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>73002</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc730-mnc08/config.xml b/core/res/res/values-mcc730-mnc08/config.xml
deleted file mode 100644
index 836ddf9..0000000
--- a/core/res/res/values-mcc730-mnc08/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2015, 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 my 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>73002</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc730-mnc10/config.xml b/core/res/res/values-mcc730-mnc10/config.xml
deleted file mode 100644
index 58b7d78..0000000
--- a/core/res/res/values-mcc730-mnc10/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2015, 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 my 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Don't use roaming icon for considered operators -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>73001</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 37d03ad..7dbd44a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2182,15 +2182,6 @@
          when alpha identifier is not provided by the UICC -->
     <bool name="config_stkNoAlphaUsrCnf">true</bool>
 
-    <!-- Don't use roaming icon for considered operators.
-         A match on config_sameNamedOperatorConsideredRoaming supersedes a match on this.
-         Can use mcc or mcc+mnc as item. For example, 302 or 21407.
-         If operators, 21404 and 21407, make roaming agreements, user of 21404 should not see
-         the roaming icon as using 21407 network.
-         To do this, add 21407 item to values-mcc214-mnc04/config.xml -->
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-    </string-array>
-
     <!-- Threshold (in ms) under which a screen off / screen on will be considered a reset of the
          immersive mode confirmation prompt.-->
     <integer name="config_immersive_mode_confirmation_panic">5000</integer>
@@ -2198,17 +2189,6 @@
     <!-- For some operators, PDU has garbages. To fix it, need to use valid index -->
     <integer name="config_valid_wappush_index">-1</integer>
 
-    <!-- This is NOT just for same named operators unlike the name suggests (will blacklist regardless of name).
-         A match on this supersedes a match on config_operatorConsideredNonRoaming.
-         Uses "startsWith" so you can use a leading substring like the mcc or
-         use the complete mcc+mnc string.
-         For a given mcc/mcc-mnc, some operators may want to roam (even if
-         config_operatorConsideredNonRoaming has the mcc/mcc-mnc).
-         user of 40485 should see the roaming icon as using 40483 network
-         though same Reliance network.
-         To do this, add 40483 item to values-mcc404-mnc85/config.xml -->
-    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
-    </string-array>
     <!-- call barring MMI code from TS 22.030 Annex B -->
     <string-array translatable="false" name="config_callBarringMMI">
         <item>33</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 270a215..7363a9b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1164,8 +1164,6 @@
   <java-symbol type="array" name="config_cdma_dun_supported_types" />
   <java-symbol type="array" name="config_disabledUntilUsedPreinstalledImes" />
   <java-symbol type="array" name="config_disabledUntilUsedPreinstalledCarrierApps" />
-  <java-symbol type="array" name="config_operatorConsideredNonRoaming" />
-  <java-symbol type="array" name="config_sameNamedOperatorConsideredRoaming" />
   <java-symbol type="array" name="config_callBarringMMI" />
   <java-symbol type="array" name="config_globalActionsList" />
   <java-symbol type="array" name="config_telephonyHardware" />
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
index a9357c9..7f4819b 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -107,7 +107,7 @@
                 "2016a", formatVersion(1, 1), true /* operationInProgress */,
                 RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
                 RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */);
-        checkParcelableRoundTrip(rulesStateWithNulls);
+        checkParcelableRoundTrip(rulesStateWithUnknowns);
     }
 
     private static void checkParcelableRoundTrip(RulesState rulesState) {
@@ -121,55 +121,14 @@
     }
 
     @Test
-    public void isSystemVersionOlderThan() {
+    public void isSystemVersionNewerThan() {
         RulesState rulesState = new RulesState(
                 "2016b", formatVersion(1, 1), false /* operationInProgress */,
                 RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
                 RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 3));
-        assertFalse(rulesState.isSystemVersionOlderThan(rulesVersion("2016a", 1)));
-        assertFalse(rulesState.isSystemVersionOlderThan(rulesVersion("2016b", 1)));
-        assertTrue(rulesState.isSystemVersionOlderThan(rulesVersion("2016c", 1)));
-    }
-
-    @Test
-    public void isInstalledDistroOlderThan() {
-        RulesState operationInProgress = new RulesState(
-                "2016b", formatVersion(1, 1), true /* operationInProgress */,
-                RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
-                RulesState.STAGED_OPERATION_UNKNOWN, null /* installedDistroRulesVersion */);
-        try {
-            operationInProgress.isInstalledDistroOlderThan(rulesVersion("2016b", 1));
-            fail();
-        } catch (IllegalStateException expected) {
-        }
-
-        RulesState nothingInstalled = new RulesState(
-                "2016b", formatVersion(1, 1), false /* operationInProgress */,
-                RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
-                RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */);
-        try {
-            nothingInstalled.isInstalledDistroOlderThan(rulesVersion("2016b", 1));
-            fail();
-        } catch (IllegalStateException expected) {
-        }
-
-        DistroRulesVersion installedVersion = rulesVersion("2016b", 3);
-        RulesState rulesStateWithInstalledVersion = new RulesState(
-                "2016b", formatVersion(1, 1), false /* operationInProgress */,
-                RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
-                RulesState.DISTRO_STATUS_INSTALLED, installedVersion);
-
-        DistroRulesVersion olderRules = rulesVersion("2016a", 1);
-        assertEquals(installedVersion.isOlderThan(olderRules),
-                rulesStateWithInstalledVersion.isInstalledDistroOlderThan(olderRules));
-
-        DistroRulesVersion sameRules = rulesVersion("2016b", 1);
-        assertEquals(installedVersion.isOlderThan(sameRules),
-                rulesStateWithInstalledVersion.isInstalledDistroOlderThan(sameRules));
-
-        DistroRulesVersion newerRules = rulesVersion("2016c", 1);
-        assertEquals(installedVersion.isOlderThan(newerRules),
-                rulesStateWithInstalledVersion.isInstalledDistroOlderThan(newerRules));
+        assertTrue(rulesState.isSystemVersionNewerThan(rulesVersion("2016a", 1)));
+        assertFalse(rulesState.isSystemVersionNewerThan(rulesVersion("2016b", 1)));
+        assertFalse(rulesState.isSystemVersionNewerThan(rulesVersion("2016c", 1)));
     }
 
     private static void assertEqualsContract(RulesState one, RulesState two) {
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index d13f37a..7480ad1 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -47,6 +47,9 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+
 import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
@@ -55,6 +58,7 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 public class CaptivePortalLoginActivity extends Activity {
     private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName();
@@ -63,7 +67,14 @@
 
     private static final int SOCKET_TIMEOUT_MS = 10000;
 
-    private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
+    private enum Result {
+        DISMISSED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED),
+        UNWANTED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED),
+        WANTED_AS_IS(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS);
+
+        final int metricsEvent;
+        Result(int metricsEvent) { this.metricsEvent = metricsEvent; }
+    };
 
     private URL mUrl;
     private String mUserAgent;
@@ -73,10 +84,15 @@
     private ConnectivityManager mCm;
     private boolean mLaunchBrowser = false;
     private MyWebViewClient mWebViewClient;
+    // Ensures that done() happens once exactly, handling concurrent callers with atomic operations.
+    private final AtomicBoolean isDone = new AtomicBoolean(false);
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
+
         mCm = ConnectivityManager.from(this);
         mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
         mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
@@ -166,13 +182,14 @@
     }
 
     private void done(Result result) {
+        if (isDone.getAndSet(true)) {
+            // isDone was already true: done() already called
+            return;
+        }
         if (DBG) {
             Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString()));
         }
-        if (mNetworkCallback != null) {
-            mCm.unregisterNetworkCallback(mNetworkCallback);
-            mNetworkCallback = null;
-        }
+        logMetricsEvent(result.metricsEvent);
         switch (result) {
             case DISMISSED:
                 mCaptivePortal.reportCaptivePortalDismissed();
@@ -231,8 +248,8 @@
     public void onDestroy() {
         super.onDestroy();
         if (mNetworkCallback != null) {
+            // mNetworkCallback is not null if mUrl is not null.
             mCm.unregisterNetworkCallback(mNetworkCallback);
-            mNetworkCallback = null;
         }
         if (mLaunchBrowser) {
             // Give time for this network to become default. After 500ms just proceed.
@@ -381,6 +398,7 @@
 
         @Override
         public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+            logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
             Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " +
                     // Only show host to avoid leaking private info.
                     Uri.parse(error.getUrl()).getHost() + " certificate: " +
@@ -492,4 +510,8 @@
         }
         return url.getHost();
     }
+
+    private void logMetricsEvent(int event) {
+        MetricsLogger.action(this, event, getPackageName());
+    }
 }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 8bc7520..58f1cd8 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2278,6 +2278,11 @@
     // OS: O DR
     ACTION_WIFI_SIGNIN = 1008;
 
+    // CaptivePortalLoginActivity displays SSL error page
+    // CATEGORY: GLOBAL_SYSTEM_UI
+    // OS: O DR
+    CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR = 1013;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/core/Android.mk b/services/core/Android.mk
index f9b0d2f..5e5c69d 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -25,7 +25,7 @@
     android.hidl.manager-V1.0-java \
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    tzdata_shared2 \
+    time_zone_distro \
     tzdata_update2 \
     android.hidl.base-V1.0-java-static \
     android.hardware.tetheroffload.control-V1.0-java-static \
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index ac2f4d0..15932cc 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1029,6 +1029,15 @@
     }
 
     @Override
+    public void setIPv6AddrGenMode(String iface, int mode) throws ServiceSpecificException {
+        try {
+            mNetdService.setIPv6AddrGenMode(iface, mode);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
     public void disableIpv6(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 4a5129d..3327bec 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -877,6 +877,7 @@
         }
 
         if (!TextUtils.isEmpty(ifname)) {
+            maybeTrackNewInterfaceLocked(ifname, ConnectivityManager.TETHERING_WIFI);
             changeInterfaceState(ifname, ipServingMode);
         } else {
             tetherMatchingInterfaces(ipServingMode, ConnectivityManager.TETHERING_WIFI);
@@ -1258,10 +1259,10 @@
                         sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
                     }
                 }
-                setUpstreamByType(ns);
+                setUpstreamNetwork(ns);
             }
 
-            protected void setUpstreamByType(NetworkState ns) {
+            protected void setUpstreamNetwork(NetworkState ns) {
                 String iface = null;
                 if (ns != null && ns.linkProperties != null) {
                     // Find the interface with the default IPv4 route. It may be the
@@ -1786,7 +1787,9 @@
             }
         }
 
-        mLog.log(String.format("OBSERVED LinkProperties update iface=%s state=%s", iface, state));
+        mLog.log(String.format(
+                "OBSERVED LinkProperties update iface=%s state=%s lp=%s",
+                iface, IControlsTethering.getStateString(state), newLp));
         final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
         mTetherMasterSM.sendMessage(which, state, 0, newLp);
     }
@@ -1798,7 +1801,10 @@
             mLog.log(iface + " is not a tetherable iface, ignoring");
             return;
         }
+        maybeTrackNewInterfaceLocked(iface, interfaceType);
+    }
 
+    private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) {
         // If we have already started a TISM for this interface, skip.
         if (mTetherStates.containsKey(iface)) {
             mLog.log("active iface (" + iface + ") reported as added, ignoring");
diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
index aaa63b1..2b81347 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
@@ -33,6 +33,16 @@
     public static final int STATE_TETHERED    = 2;
     public static final int STATE_LOCAL_ONLY  = 3;
 
+    public static String getStateString(int state) {
+        switch (state) {
+            case STATE_UNAVAILABLE: return "UNAVAILABLE";
+            case STATE_AVAILABLE:   return "AVAILABLE";
+            case STATE_TETHERED:    return "TETHERED";
+            case STATE_LOCAL_ONLY:  return "LOCAL_ONLY";
+        }
+        return "UNKNOWN: " + state;
+    }
+
     /**
      * Notify that |who| has changed its tethering state.
      *
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 3aca45f..08deef8 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -85,13 +85,16 @@
             mLog.i("tethering offload control not supported");
             stop();
         }
+        mLog.log("tethering offload started");
     }
 
     public void stop() {
+        final boolean wasStarted = started();
         mUpstreamLinkProperties = null;
         mHwInterface.stopOffloadControl();
         mControlInitialized = false;
         mConfigInitialized = false;
+        if (wasStarted) mLog.log("tethering offload stopped");
     }
 
     public void setUpstreamLinkProperties(LinkProperties lp) {
@@ -161,6 +164,7 @@
             }
         }
 
-        return mHwInterface.setUpstreamParameters(iface, v4addr, v4gateway, v6gateways);
+        return mHwInterface.setUpstreamParameters(
+                iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 3ecf0d1..1fc1684 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.tethering;
 
+import static com.android.internal.util.BitUtils.uint16;
+
 import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
 import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
@@ -33,6 +35,9 @@
  */
 public class OffloadHardwareInterface {
     private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
+    private static final String NO_INTERFACE_NAME = "";
+    private static final String NO_IPV4_ADDRESS = "";
+    private static final String NO_IPV4_GATEWAY = "";
 
     private static native boolean configOffload();
 
@@ -107,6 +112,11 @@
 
     public boolean setUpstreamParameters(
             String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
+        iface = iface != null ? iface : NO_INTERFACE_NAME;
+        v4addr = v4addr != null ? v4addr : NO_IPV4_ADDRESS;
+        v4gateway = v4gateway != null ? v4gateway : NO_IPV4_GATEWAY;
+        v6gws = v6gws != null ? v6gws : new ArrayList<>();
+
         final CbResults results = new CbResults();
         try {
             mOffloadControl.setUpstreamParameters(
@@ -143,8 +153,8 @@
             handler.post(() -> {
                     controlCb.onNatTimeoutUpdate(
                         params.proto,
-                        params.src.addr, params.src.port,
-                        params.dst.addr, params.dst.port);
+                        params.src.addr, uint16(params.src.port),
+                        params.dst.addr, uint16(params.dst.port));
             });
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index bff13d4..86b2551 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -56,9 +56,10 @@
 import java.util.Random;
 
 /**
- * @hide
+ * Provides the interface to IP-layer serving functionality for a given network
+ * interface, e.g. for tethering or "local-only hotspot" mode.
  *
- * Tracks the eligibility of a given network interface for tethering.
+ * @hide
  */
 public class TetherInterfaceStateMachine extends StateMachine {
     private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
@@ -117,6 +118,12 @@
     private String mMyUpstreamIfaceName;  // may change over time
     private NetworkInterface mNetworkInterface;
     private byte[] mHwAddr;
+    // TODO: De-duplicate this with mLinkProperties above. Currently, these link
+    // properties are those selected by the IPv6TetheringCoordinator and relayed
+    // to us. By comparison, mLinkProperties contains the addresses and directly
+    // connected routes that have been formed from these properties iff. we have
+    // succeeded in configuring them and are able to announce them within Router
+    // Advertisements (otherwise, we do not add them to mLinkProperties at all).
     private LinkProperties mLastIPv6LinkProperties;
     private RouterAdvertisementDaemon mRaDaemon;
     private RaParams mLastRaParams;
@@ -133,7 +140,7 @@
         mIfaceName = ifaceName;
         mInterfaceType = interfaceType;
         mLinkProperties = new LinkProperties();
-        mLinkProperties.setInterfaceName(mIfaceName);
+        resetLinkProperties();
         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
 
         mInitialState = new InitialState();
@@ -162,10 +169,15 @@
      * Internals.
      */
 
-    // configured when we start tethering and unconfig'd on error or conclusion
-    private boolean configureIfaceIp(boolean enabled) {
-        if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
+    private boolean startIPv4() { return configureIPv4(true); }
 
+    private void stopIPv4() { configureIPv4(false); }
+
+    private boolean configureIPv4(boolean enabled) {
+        if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
+
+        // TODO: Replace this hard-coded information with dynamically selected
+        // config passed down to us by a higher layer IP-coordinating element.
         String ipAsString = null;
         int prefixLen = 0;
         if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
@@ -179,32 +191,45 @@
             return true;
         }
 
-        InterfaceConfiguration ifcg = null;
+        final LinkAddress linkAddr;
         try {
-            ifcg = mNMService.getInterfaceConfig(mIfaceName);
-            if (ifcg != null) {
-                InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
-                ifcg.setLinkAddress(new LinkAddress(addr, prefixLen));
-                if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
-                    // The WiFi stack has ownership of the interface up/down state.
-                    // It is unclear whether the bluetooth or USB stacks will manage their own
-                    // state.
-                    ifcg.ignoreInterfaceUpDownStatus();
-                } else {
-                    if (enabled) {
-                        ifcg.setInterfaceUp();
-                    } else {
-                        ifcg.setInterfaceDown();
-                    }
-                }
-                ifcg.clearFlag("running");
-                mNMService.setInterfaceConfig(mIfaceName, ifcg);
+            final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
+            if (ifcg == null) {
+                mLog.e("Received null interface config");
+                return false;
             }
+
+            InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
+            linkAddr = new LinkAddress(addr, prefixLen);
+            ifcg.setLinkAddress(linkAddr);
+            if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
+                // The WiFi stack has ownership of the interface up/down state.
+                // It is unclear whether the Bluetooth or USB stacks will manage their own
+                // state.
+                ifcg.ignoreInterfaceUpDownStatus();
+            } else {
+                if (enabled) {
+                    ifcg.setInterfaceUp();
+                } else {
+                    ifcg.setInterfaceDown();
+                }
+            }
+            ifcg.clearFlag("running");
+            mNMService.setInterfaceConfig(mIfaceName, ifcg);
         } catch (Exception e) {
             mLog.e("Error configuring interface " + e);
             return false;
         }
 
+        // Directly-connected route.
+        final RouteInfo route = new RouteInfo(linkAddr);
+        if (enabled) {
+            mLinkProperties.addLinkAddress(linkAddr);
+            mLinkProperties.addRoute(route);
+        } else {
+            mLinkProperties.removeLinkAddress(linkAddr);
+            mLinkProperties.removeRoute(route);
+        }
         return true;
     }
 
@@ -294,7 +319,7 @@
         mLastIPv6LinkProperties = v6only;
     }
 
-    private void configureLocalRoutes(
+    private void configureLocalIPv6Routes(
             HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
         // [1] Remove the routes that are deprecated.
         if (!deprecatedPrefixes.isEmpty()) {
@@ -309,6 +334,8 @@
             } catch (RemoteException e) {
                 mLog.e("Failed to remove IPv6 routes from local table: " + e);
             }
+
+            for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
         }
 
         // [2] Add only the routes that have not previously been added.
@@ -340,11 +367,13 @@
                 } catch (RemoteException e) {
                     mLog.e("Failed to add IPv6 routes to local table: " + e);
                 }
+
+                for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
             }
         }
     }
 
-    private void configureLocalDns(
+    private void configureLocalIPv6Dns(
             HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
         final INetd netd = NetdService.getInstance();
         if (netd == null) {
@@ -362,6 +391,8 @@
                 } catch (ServiceSpecificException | RemoteException e) {
                     mLog.e("Failed to remove local dns IP " + dnsString + ": " + e);
                 }
+
+                mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
             }
         }
 
@@ -380,6 +411,8 @@
                     mLog.e("Failed to add local dns IP " + dnsString + ": " + e);
                     newDnses.remove(dns);
                 }
+
+                mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
             }
         }
 
@@ -396,10 +429,10 @@
             final RaParams deprecatedParams =
                     RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
 
-            configureLocalRoutes(deprecatedParams.prefixes,
+            configureLocalIPv6Routes(deprecatedParams.prefixes,
                     (newParams != null) ? newParams.prefixes : null);
 
-            configureLocalDns(deprecatedParams.dnses,
+            configureLocalIPv6Dns(deprecatedParams.dnses,
                     (newParams != null) ? newParams.dnses : null);
 
             mRaDaemon.buildNewRa(deprecatedParams, newParams);
@@ -419,12 +452,19 @@
     private void sendInterfaceState(int newInterfaceState) {
         mTetherController.updateInterfaceState(
                 TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
-        // TODO: Populate mLinkProperties correctly, and send more sensible
-        // updates more frequently (not just here).
+        sendLinkProperties();
+    }
+
+    private void sendLinkProperties() {
         mTetherController.updateLinkProperties(
                 TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
     }
 
+    private void resetLinkProperties() {
+        mLinkProperties.clear();
+        mLinkProperties.setInterfaceName(mIfaceName);
+    }
+
     class InitialState extends State {
         @Override
         public void enter() {
@@ -464,7 +504,7 @@
     class BaseServingState extends State {
         @Override
         public void enter() {
-            if (!configureIfaceIp(true)) {
+            if (!startIPv4()) {
                 mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
                 return;
             }
@@ -498,7 +538,9 @@
                 mLog.e("Failed to untether interface: " + e);
             }
 
-            configureIfaceIp(false);
+            stopIPv4();
+
+            resetLinkProperties();
         }
 
         @Override
@@ -515,6 +557,7 @@
                     break;
                 case CMD_IPV6_TETHER_UPDATE:
                     updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+                    sendLinkProperties();
                     break;
                 case CMD_IP_FORWARDING_ENABLE_ERROR:
                 case CMD_IP_FORWARDING_DISABLE_ERROR:
@@ -625,7 +668,6 @@
             if (super.processMessage(message)) return true;
 
             maybeLogMessage(this, message.what);
-            boolean retValue = true;
             switch (message.what) {
                 case CMD_TETHER_REQUESTED:
                     mLog.e("CMD_TETHER_REQUESTED while already tethering.");
@@ -655,10 +697,9 @@
                     mMyUpstreamIfaceName = newUpstreamIfaceName;
                     break;
                 default:
-                    retValue = false;
-                    break;
+                    return false;
             }
-            return retValue;
+            return true;
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 651de89..9b034ae 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity.tethering;
 
 import static android.content.Context.TELEPHONY_SERVICE;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -208,29 +209,26 @@
         // *always* an upstream, regardless of the upstream interface types
         // specified by configuration resources.
         if (dunCheck == DUN_REQUIRED) {
-            if (!upstreamIfaceTypes.contains(TYPE_MOBILE_DUN)) {
-                upstreamIfaceTypes.add(TYPE_MOBILE_DUN);
-            }
+            appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN);
         } else if (dunCheck == DUN_NOT_REQUIRED) {
-            if (!upstreamIfaceTypes.contains(TYPE_MOBILE)) {
-                upstreamIfaceTypes.add(TYPE_MOBILE);
-            }
-            if (!upstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)) {
-                upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
-            }
+            appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE);
+            appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_HIPRI);
         } else {
             // Fix upstream interface types for case DUN_UNSPECIFIED.
             // Do not modify if a cellular interface type is already present in the
             // upstream interface types. Add TYPE_MOBILE and TYPE_MOBILE_HIPRI if no
             // cellular interface types are found in the upstream interface types.
-            if (!(upstreamIfaceTypes.contains(TYPE_MOBILE_DUN)
-                    || upstreamIfaceTypes.contains(TYPE_MOBILE)
-                    || upstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI))) {
+            if (!(containsOneOf(upstreamIfaceTypes,
+                    TYPE_MOBILE_DUN, TYPE_MOBILE, TYPE_MOBILE_HIPRI))) {
                 upstreamIfaceTypes.add(TYPE_MOBILE);
                 upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
             }
         }
 
+        // Always make sure our good friend Ethernet is present.
+        // TODO: consider unilaterally forcing this at the front.
+        prependIfNotPresent(upstreamIfaceTypes, TYPE_ETHERNET);
+
         return upstreamIfaceTypes;
     }
 
@@ -253,4 +251,21 @@
     private static String[] copy(String[] strarray) {
         return Arrays.copyOf(strarray, strarray.length);
     }
+
+    private static void prependIfNotPresent(ArrayList<Integer> list, int value) {
+        if (list.contains(value)) return;
+        list.add(0, value);
+    }
+
+    private static void appendIfNotPresent(ArrayList<Integer> list, int value) {
+        if (list.contains(value)) return;
+        list.add(value);
+    }
+
+    private static boolean containsOneOf(ArrayList<Integer> list, Integer... values) {
+        for (Integer value : values) {
+            if (list.contains(value)) return true;
+        }
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 82bd356..58bdeb9 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -18,6 +18,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.SystemService;
+import com.android.timezone.distro.DistroException;
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.TimeZoneDistro;
+import com.android.timezone.distro.StagedDistroOperation;
 
 import android.app.timezone.Callback;
 import android.app.timezone.DistroFormatVersion;
@@ -36,9 +40,6 @@
 import java.util.Arrays;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
-import libcore.tzdata.shared2.DistroException;
-import libcore.tzdata.shared2.DistroVersion;
-import libcore.tzdata.shared2.StagedDistroOperation;
 import libcore.tzdata.update2.TimeZoneDistroInstaller;
 
 // TODO(nfuller) Add EventLog calls where useful in the system server.
@@ -224,7 +225,8 @@
             try {
                 byte[] distroBytes =
                         RulesManagerService.this.mFileDescriptorHelper.readFully(mTimeZoneDistro);
-                int installerResult = mInstaller.stageInstallWithErrorCode(distroBytes);
+                TimeZoneDistro distro = new TimeZoneDistro(distroBytes);
+                int installerResult = mInstaller.stageInstallWithErrorCode(distro);
                 int resultCode = mapInstallerResultToApiCode(installerResult);
                 sendFinishedStatus(mCallback, resultCode);
 
diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
index 3c73c88..2be69ac 100644
--- a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
@@ -16,6 +16,8 @@
 
 package com.android.server.updates;
 
+import com.android.timezone.distro.TimeZoneDistro;
+
 import android.util.Slog;
 
 import java.io.File;
@@ -46,7 +48,8 @@
 
     @Override
     protected void install(byte[] content, int version) throws IOException {
-        boolean valid = installer.install(content);
+        TimeZoneDistro distro = new TimeZoneDistro(content);
+        boolean valid = installer.install(distro);
         Slog.i(TAG, "Timezone data install valid for this device: " + valid);
         // Even if !valid, we call super.install(). Only in the event of an exception should we
         // not. If we didn't do this we could attempt to install repeatedly.
diff --git a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
index 241ccf6..4e5c27f 100644
--- a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
+++ b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
@@ -71,7 +71,7 @@
 // auto-close it (otherwise there would be double-close problems).
 //
 // Rely upon the compiler to eliminate the constexprs used for clarity.
-hidl_handle&& handleFromFileDescriptor(base::unique_fd fd) {
+hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
     hidl_handle h;
 
     NATIVE_HANDLE_DECLARE_STORAGE(storage, 0, 0);
@@ -83,7 +83,7 @@
     static constexpr bool kTakeOwnership = true;
     h.setTo(nh, kTakeOwnership);
 
-    return std::move(h);
+    return h;
 }
 
 }  // namespace
@@ -116,13 +116,14 @@
 
     bool rval;
     hidl_string msg;
-    configInterface->setHandles(h1, h2,
+    const auto status = configInterface->setHandles(h1, h2,
             [&rval, &msg](bool success, const hidl_string& errMsg) {
                 rval = success;
                 msg = errMsg;
             });
-    if (!rval) {
-        ALOGE("IOffloadConfig::setHandles() error: %s", msg.c_str());
+    if (!status.isOk() || !rval) {
+        ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
+              status.description().c_str(), msg.c_str());
     }
 
     return rval;
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 6608167..7b57bbd 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -20,16 +20,17 @@
 import com.android.internal.util.WakeupMessage;
 
 import android.content.Context;
-import android.net.apf.ApfCapabilities;
-import android.net.apf.ApfFilter;
 import android.net.DhcpResults;
+import android.net.INetd;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
-import android.net.LinkProperties;
 import android.net.LinkProperties.ProvisioningChange;
+import android.net.LinkProperties;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.StaticIpConfiguration;
+import android.net.apf.ApfCapabilities;
+import android.net.apf.ApfFilter;
 import android.net.dhcp.DhcpClient;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpManagerEvent;
@@ -38,7 +39,9 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
+import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
@@ -319,6 +322,16 @@
                 return this;
             }
 
+            public Builder withIPv6AddrGenModeEUI64() {
+                mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64;
+                return this;
+            }
+
+            public Builder withIPv6AddrGenModeStablePrivacy() {
+                mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
+                return this;
+            }
+
             public ProvisioningConfiguration build() {
                 return new ProvisioningConfiguration(mConfig);
             }
@@ -331,6 +344,7 @@
         /* package */ StaticIpConfiguration mStaticIpConfig;
         /* package */ ApfCapabilities mApfCapabilities;
         /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
+        /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
 
         public ProvisioningConfiguration() {}
 
@@ -354,6 +368,7 @@
                     .add("mStaticIpConfig: " + mStaticIpConfig)
                     .add("mApfCapabilities: " + mApfCapabilities)
                     .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
+                    .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
                     .toString();
         }
     }
@@ -1044,16 +1059,25 @@
         return true;
     }
 
+    private void setIPv6AddrGenModeIfSupported() throws RemoteException {
+        try {
+            mNwService.setIPv6AddrGenMode(mInterfaceName, mConfiguration.mIPv6AddrGenMode);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode != OsConstants.EOPNOTSUPP) {
+                throw e;
+            }
+        }
+    }
+
     private boolean startIPv6() {
         // Set privacy extensions.
         try {
             mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
+
+            setIPv6AddrGenModeIfSupported();
             mNwService.enableIpv6(mInterfaceName);
-        } catch (RemoteException re) {
-            logError("Unable to change interface settings: %s", re);
-            return false;
-        } catch (IllegalStateException ie) {
-            logError("Unable to change interface settings: %s", ie);
+        } catch (IllegalStateException | RemoteException | ServiceSpecificException e) {
+            logError("Unable to change interface settings: %s", e);
             return false;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
index a7f4c99..86116a9 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -16,6 +16,10 @@
 
 package com.android.server.timezone;
 
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.TimeZoneDistro;
+import com.android.timezone.distro.StagedDistroOperation;
+
 import org.junit.Before;
 import org.junit.Test;
 
@@ -29,8 +33,6 @@
 import java.io.IOException;
 import java.util.concurrent.Executor;
 import javax.annotation.Nullable;
-import libcore.tzdata.shared2.DistroVersion;
-import libcore.tzdata.shared2.StagedDistroOperation;
 import libcore.tzdata.update2.TimeZoneDistroInstaller;
 
 import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION;
@@ -402,14 +404,16 @@
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
 
+        TimeZoneDistro expectedDistro = new TimeZoneDistro(expectedContent);
+
         // Set up the installer.
-        configureStageInstallExpectation(expectedContent, TimeZoneDistroInstaller.INSTALL_SUCCESS);
+        configureStageInstallExpectation(expectedDistro, TimeZoneDistroInstaller.INSTALL_SUCCESS);
 
         // Simulate the async execution.
         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
 
         // Verify the expected calls were made to other components.
-        verifyStageInstallCalled(expectedContent);
+        verifyStageInstallCalled(expectedDistro);
         verifyPackageTrackerCalled(token, true /* success */);
 
         // Check the callback was called.
@@ -435,14 +439,16 @@
         verifyNoInstallerCallsMade();
         callback.assertNoResultReceived();
 
+        TimeZoneDistro expectedDistro = new TimeZoneDistro(expectedContent);
+
         // Set up the installer.
-        configureStageInstallExpectation(expectedContent, TimeZoneDistroInstaller.INSTALL_SUCCESS);
+        configureStageInstallExpectation(expectedDistro, TimeZoneDistroInstaller.INSTALL_SUCCESS);
 
         // Simulate the async execution.
         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
 
         // Verify the expected calls were made to other components.
-        verifyStageInstallCalled(expectedContent);
+        verifyStageInstallCalled(expectedDistro);
         verifyPackageTrackerCalled(null /* expectedToken */, true /* success */);
 
         // Check the callback was received.
@@ -470,15 +476,17 @@
         verifyNoInstallerCallsMade();
         callback.assertNoResultReceived();
 
+        TimeZoneDistro expectedDistro = new TimeZoneDistro(expectedContent);
+
         // Set up the installer.
         configureStageInstallExpectation(
-                expectedContent, TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR);
+                expectedDistro, TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR);
 
         // Simulate the async execution.
         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
 
         // Verify the expected calls were made to other components.
-        verifyStageInstallCalled(expectedContent);
+        verifyStageInstallCalled(expectedDistro);
 
         // Validation failure is treated like a successful check: repeating it won't improve things.
         boolean expectedSuccess = true;
@@ -779,9 +787,9 @@
                 .thenThrow(new IOException("Simulated failure"));
     }
 
-    private void configureStageInstallExpectation(byte[] expectedContent, int resultCode)
+    private void configureStageInstallExpectation(TimeZoneDistro expected, int resultCode)
             throws Exception {
-        when(mMockTimeZoneDistroInstaller.stageInstallWithErrorCode(eq(expectedContent)))
+        when(mMockTimeZoneDistroInstaller.stageInstallWithErrorCode(eq(expected)))
                 .thenReturn(resultCode);
     }
 
@@ -789,8 +797,8 @@
         doReturn(success).when(mMockTimeZoneDistroInstaller).stageUninstall();
     }
 
-    private void verifyStageInstallCalled(byte[] expectedContent) throws Exception {
-        verify(mMockTimeZoneDistroInstaller).stageInstallWithErrorCode(eq(expectedContent));
+    private void verifyStageInstallCalled(TimeZoneDistro expected) throws Exception {
+        verify(mMockTimeZoneDistroInstaller).stageInstallWithErrorCode(eq(expected));
         verifyNoMoreInteractions(mMockTimeZoneDistroInstaller);
         reset(mMockTimeZoneDistroInstaller);
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e3d66e7..17ad779 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1347,6 +1347,30 @@
     public static final String KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL =
             "disable_voice_barring_notification_bool";
 
+    /**
+     * List of operators considered non-roaming which won't show roaming icon.
+     * <p>
+     * Can use mcc or mcc+mnc as item. For example, 302 or 21407.
+     * If operators, 21404 and 21407, make roaming agreements, users of 21404 should not see
+     * the roaming icon as using 21407 network.
+     * @hide
+     */
+    public static final String KEY_NON_ROAMING_OPERATOR_STRING_ARRAY =
+            "non_roaming_operator_string_array";
+
+    /**
+     * List of operators considered roaming with the roaming icon.
+     * <p>
+     * Can use mcc or mcc+mnc as item. For example, 302 or 21407.
+     * If operators, 21404 and 21407, make roaming agreements, users of 21404 should see
+     * the roaming icon as using 21407 network.
+     * <p>
+     * A match on this supersedes a match on {@link #KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}.
+     * @hide
+     */
+    public static final String KEY_ROAMING_OPERATOR_STRING_ARRAY =
+            "roaming_operator_string_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1583,6 +1607,8 @@
         sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
+	sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
     }
 
     /**
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
index c8c6d01..79ee37a 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadManager.java
@@ -16,10 +16,13 @@
 
 package android.telephony;
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -27,13 +30,18 @@
 import android.telephony.mbms.DownloadRequest;
 import android.telephony.mbms.DownloadStatus;
 import android.telephony.mbms.IMbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadReceiver;
 import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsTempFileProvider;
 import android.telephony.mbms.MbmsUtils;
 import android.telephony.mbms.vendor.IMbmsDownloadService;
 import android.util.Log;
 
+import java.io.File;
+import java.io.IOException;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
 
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
@@ -47,7 +55,7 @@
      * The MBMS middleware should send this when a download of single file has completed or
      * failed. Mandatory extras are
      * {@link #EXTRA_RESULT}
-     * {@link #EXTRA_INFO}
+     * {@link #EXTRA_FILE_INFO}
      * {@link #EXTRA_REQUEST}
      * {@link #EXTRA_TEMP_LIST}
      * {@link #EXTRA_FINAL_URI}
@@ -93,7 +101,7 @@
      * is for. Must not be null.
      * TODO: future systemapi (here and and all extras) except the two for the app intent
      */
-    public static final String EXTRA_INFO = "android.telephony.mbms.extra.INFO";
+    public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
 
     /**
      * Extra containing the {@link DownloadRequest} for which the download result or file
@@ -144,6 +152,14 @@
             "android.telephony.mbms.extra.PAUSED_URI_LIST";
 
     /**
+     * Extra containing a string that points to the middleware's knowledge of where the temp file
+     * root for the app is. The path should be a canonical path as returned by
+     * {@link File#getCanonicalPath()}
+     */
+    public static final String EXTRA_TEMP_FILE_ROOT =
+            "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+
+    /**
      * Extra containing a list of {@link Uri}s indicating temp files which the middleware is
      * still using.
      */
@@ -165,12 +181,10 @@
     public static final int RESULT_EXPIRED    = 3;
     // TODO - more results!
 
-    private static final long BIND_TIMEOUT_MS = 3000;
-
     private final Context mContext;
-    private int mSubId = INVALID_SUBSCRIPTION_ID;
+    private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
-    private IMbmsDownloadService mService;
+    private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
     private final IMbmsDownloadManagerCallback mCallback;
     private final String mDownloadAppName;
 
@@ -179,116 +193,221 @@
         mContext = context;
         mCallback = callback;
         mDownloadAppName = downloadAppName;
-        mSubId = subId;
+        mSubscriptionId = subId;
     }
 
     /**
      * Create a new MbmsDownloadManager using the system default data subscription ID.
-     *
-     * Note that this call will bind a remote service and that may take a bit.  This
-     * may throw an Illegal ArgumentException or RemoteException.
+     * See {@link #create(Context, IMbmsDownloadManagerCallback, String, int)}
      *
      * @hide
      */
-    public static MbmsDownloadManager createManager(Context context,
+    public static MbmsDownloadManager create(Context context,
             IMbmsDownloadManagerCallback listener, String downloadAppName)
             throws MbmsException {
-        MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName,
+        return create(context, listener, downloadAppName,
                 SubscriptionManager.getDefaultSubscriptionId());
-        mdm.bindAndInitialize();
-        return mdm;
     }
 
     /**
      * Create a new MbmsDownloadManager using the given subscription ID.
      *
-     * Note that this call will bind a remote service and that may take a bit.  This
-     * may throw an Illegal ArgumentException or RemoteException.
+     * Note that this call will bind a remote service and that may take a bit. The instance of
+     * {@link MbmsDownloadManager} that is returned will not be ready for use until
+     * {@link IMbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
+     * If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown.
      *
+     * This also may throw an {@link IllegalArgumentException} or a {@link MbmsException}.
+     *
+     * @param context The instance of {@link Context} to use
+     * @param listener A callback to get asynchronous error messages and file service updates.
+     * @param downloadAppName The app name, as negotiated with the eMBMS provider
+     * @param subscriptionId The data subscription ID to use
      * @hide
      */
-
-    public static MbmsDownloadManager createManager(Context context,
-            IMbmsDownloadManagerCallback listener, String downloadAppName, int subId)
+    public static MbmsDownloadManager create(Context context,
+            IMbmsDownloadManagerCallback listener, String downloadAppName, int subscriptionId)
             throws MbmsException {
         MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName,
-                subId);
+                subscriptionId);
         mdm.bindAndInitialize();
         return mdm;
     }
 
     private void bindAndInitialize() throws MbmsException {
-        // TODO: fold binding for download and streaming into a common utils class.
-        final CountDownLatch latch = new CountDownLatch(1);
-        ServiceConnection bindListener = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                mService = IMbmsDownloadService.Stub.asInterface(service);
-                latch.countDown();
-            }
+        MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
+                new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IMbmsDownloadService downloadService =
+                                IMbmsDownloadService.Stub.asInterface(service);
+                        try {
+                            downloadService.initialize(
+                                    mDownloadAppName, mSubscriptionId, mCallback);
+                        } catch (RemoteException e) {
+                            Log.e(LOG_TAG, "Service died before initialization");
+                            return;
+                        }
+                        mService.set(downloadService);
+                    }
 
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                mService = null;
-            }
-        };
-
-        Intent bindIntent = new Intent();
-        bindIntent.setComponent(MbmsUtils.toComponentName(
-                MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_DOWNLOAD_SERVICE_ACTION)));
-
-        // Kick off the binding, and synchronously wait until binding is complete
-        mContext.bindService(bindIntent, bindListener, Context.BIND_AUTO_CREATE);
-
-        MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS);
-
-        // TODO: initialize
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        mService.set(null);
+                    }
+                });
     }
 
     /**
-     * Gets the list of files published for download.
-     * They may occur at times far in the future.
-     * servicesClasses lets the app filter on types of files and is opaque data between
-     *     the app and the carrier
+     * An inspection API to retrieve the list of available
+     * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
+     * The results are returned asynchronously via a call to
+     * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
      *
-     * Multiple calls replace trhe list of serviceClasses of interest.
+     * The serviceClasses argument lets the app filter on types of programming and is opaque data
+     * negotiated beforehand between the app and the carrier.
      *
-     * May throw an IllegalArgumentException or RemoteException.
+     * Multiple calls replace the list of serviceClasses of interest.
      *
-     * Synchronous responses include
-     * <li>SUCCESS</li>
-     * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
+     * This may throw an {@link MbmsException} containing one of the following errors:
+     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+     * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+     * {@link MbmsException#ERROR_SERVICE_LOST}
      *
-     * Asynchronous errors through the listener include any of the errors except
-     * <li>ERROR_MSDC_UNABLE_TO_)START_SERVICE</li>
-     * <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
-     * <li>ERROR_MSDC_END_OF_SESSION</li>
+     * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)}
+     * callback can include any of the errors except:
+     * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}
+     * {@link MbmsException#ERROR_END_OF_SESSION}
      */
-    public int getFileServices(List<String> serviceClasses) {
-        return 0;
+    public void getFileServices(List<String> classList) throws MbmsException {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+        }
+        try {
+            int returnCode = downloadService.getFileServices(
+                    mDownloadAppName, mSubscriptionId, classList);
+            if (returnCode != MbmsException.SUCCESS) {
+                throw new MbmsException(returnCode);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+        }
     }
 
+    /**
+     * Sets the temp file root for downloads.
+     * All temp files created for the middleware to write to will be contained in the specified
+     * directory. Applications that wish to specify a location only need to call this method once
+     * as long their data is persisted in storage -- the argument will be stored both in a
+     * local instance of {@link android.content.SharedPreferences} and by the middleware.
+     *
+     * If this method is not called at least once before calling
+     * {@link #download(DownloadRequest, IDownloadCallback)}, the framework
+     * will default to a directory formed by the concatenation of the app's files directory and
+     * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
+     *
+     * This method may not be called while any download requests are still active. If this is
+     * the case, an {@link MbmsException} will be thrown with code
+     * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+     *
+     * The {@link File} supplied as a root temp file directory must already exist. If not, an
+     * {@link IllegalArgumentException} will be thrown.
+     * @param tempFileRootDirectory A directory to place temp files in.
+     */
+    public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory)
+            throws MbmsException {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+        }
+        if (!tempFileRootDirectory.exists()) {
+            throw new IllegalArgumentException("Provided directory does not exist");
+        }
+        if (!tempFileRootDirectory.isDirectory()) {
+            throw new IllegalArgumentException("Provided File is not a directory");
+        }
+        String filePath;
+        try {
+            filePath = tempFileRootDirectory.getCanonicalPath();
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
+        }
+
+        try {
+            int result = downloadService.setTempFileRootDirectory(
+                    mDownloadAppName, mSubscriptionId, filePath);
+            if (result != MbmsException.SUCCESS) {
+                throw new MbmsException(result);
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+        }
+
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
+    }
 
     /**
-     * Requests a future download.
-     * returns a token which may be used to cancel a download.
+     * Requests a download of a file that is available via multicast.
+     *
      * downloadListener is an optional callback object which can be used to get progress reports
      *     of a currently occuring download.  Note this can only run while the calling app
      *     is running, so future downloads will simply result in resultIntents being sent
      *     for completed or errored-out downloads.  A NULL indicates no callbacks are needed.
      *
-     * May throw an IllegalArgumentException or RemoteExcpetion.
+     * May throw an {@link IllegalArgumentException}
+     *
+     * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
+     * this method will create a directory at the default location defined at
+     * {@link MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
+     * file root directory.
      *
      * Asynchronous errors through the listener include any of the errors
+     *
+     * @param request The request that specifies what should be downloaded
+     * @param callback Optional callback that will provide progress updates if the app is running.
      */
-    public DownloadRequest download(DownloadRequest request, IDownloadCallback listener) {
-        request.setAppName(mDownloadAppName);
-        try {
-            mService.download(request, listener);
-        } catch (RemoteException e) {
-            mService = null;
+    public void download(DownloadRequest request, IDownloadCallback callback)
+            throws MbmsException {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
         }
-        return request;
+
+        // Check to see whether the app's set a temp root dir yet, and set it if not.
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
+            File tempRootDirectory = new File(mContext.getFilesDir(),
+                    MbmsTempFileProvider.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
+            tempRootDirectory.mkdirs();
+            setTempFileRootDirectory(tempRootDirectory);
+        }
+
+        request.setAppName(mDownloadAppName);
+        // Check if the request is a multipart download. If so, validate that the destination is
+        // a directory that exists.
+        // TODO: figure out what qualifies a request as a multipart download request.
+        if (request.getSourceUri().getLastPathSegment() != null &&
+                request.getSourceUri().getLastPathSegment().contains("*")) {
+            File toFile = new File(request.getDestinationUri().getSchemeSpecificPart());
+            if (!toFile.isDirectory()) {
+                throw new IllegalArgumentException("Multipart download must specify valid " +
+                        "destination directory.");
+            }
+        }
+        // TODO: check to make sure destination is clear
+        // TODO: write download request token
+        try {
+            downloadService.download(request, callback);
+        } catch (RemoteException e) {
+            mService.set(null);
+        }
     }
 
     /**
@@ -355,13 +474,45 @@
         return 0;
     }
 
+    /**
+     * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
+     * the various intents from the middleware should be targeted towards.
+     * @param uid The uid of the frontend app.
+     * @return The component name of the receiver that the middleware should send its intents to,
+     * or null if the app didn't declare it in the manifest.
+     *
+     * @hide
+     * future systemapi
+     */
+    public static ComponentName getAppReceiverFromUid(Context context, int uid) {
+        String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+        if (packageNames == null) {
+            return null;
+        }
+
+        for (String packageName : packageNames) {
+            ComponentName candidate = new ComponentName(packageName,
+                    MbmsDownloadReceiver.class.getCanonicalName());
+            Intent queryIntent = new Intent();
+            queryIntent.setComponent(candidate);
+            List<ResolveInfo> receivers =
+                    context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
+            if (receivers != null && receivers.size() > 0) {
+                return candidate;
+            }
+        }
+        return null;
+    }
+
     public void dispose() {
         try {
-            if (mService != null) {
-                mService.dispose(mDownloadAppName, mSubId);
-            } else {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null) {
                 Log.i(LOG_TAG, "Service already dead");
+                return;
             }
+            downloadService.dispose(mDownloadAppName, mSubscriptionId);
+            mService.set(null);
         } catch (RemoteException e) {
             // Ignore
             Log.i(LOG_TAG, "Remote exception while disposing of service");
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index f68e243..af7f333 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -18,11 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.mbms.MbmsException;
@@ -34,56 +30,18 @@
 import android.telephony.mbms.vendor.IMbmsStreamingService;
 import android.util.Log;
 
-import java.util.LinkedList;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 /** @hide */
 public class MbmsStreamingManager {
-    private interface ServiceListener {
-        void onServiceConnected();
-        void onServiceDisconnected();
-    }
-
     private static final String LOG_TAG = "MbmsStreamingManager";
     public static final String MBMS_STREAMING_SERVICE_ACTION =
             "android.telephony.action.EmbmsStreaming";
 
-    private static final boolean DEBUG = true;
-    private static final int BIND_TIMEOUT_MS = 3000;
-
-    private IMbmsStreamingService mService;
-    private ServiceConnection mServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (service != null) {
-                Log.i(LOG_TAG, String.format("Connected to service %s", name));
-                synchronized (MbmsStreamingManager.this) {
-                    mService = IMbmsStreamingService.Stub.asInterface(service);
-                    for (ServiceListener l : mServiceListeners) {
-                        l.onServiceConnected();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            Log.i(LOG_TAG, String.format("Disconnected from service %s", name));
-            synchronized (MbmsStreamingManager.this) {
-                mService = null;
-                for (ServiceListener l : mServiceListeners) {
-                    l.onServiceDisconnected();
-                }
-            }
-        }
-    };
-
-    private List<ServiceListener> mServiceListeners = new LinkedList<>();
-
+    private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
     private MbmsStreamingManagerCallback mCallbackToApp;
     private final String mAppName;
 
@@ -128,28 +86,26 @@
     public static MbmsStreamingManager create(Context context,
             MbmsStreamingManagerCallback listener, String streamingAppName)
             throws MbmsException {
-        int subId = SubscriptionManager.getDefaultSubscriptionId();
-        MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
-                streamingAppName, subId);
-        manager.bindAndInitialize();
-        return manager;
+        return create(context, listener, streamingAppName,
+                SubscriptionManager.getDefaultSubscriptionId());
     }
 
     /**
      * Terminates this instance, ending calls to the registered listener.  Also terminates
      * any streaming services spawned from this instance.
      */
-    public synchronized void dispose() {
-        if (mService == null) {
+    public void dispose() {
+        IMbmsStreamingService streamingService = mService.get();
+        if (streamingService == null) {
             // Ignore and return, assume already disposed.
             return;
         }
         try {
-            mService.dispose(mAppName, mSubscriptionId);
+            streamingService.dispose(mAppName, mSubscriptionId);
         } catch (RemoteException e) {
             // Ignore for now
         }
-        mService = null;
+        mService.set(null);
     }
 
     /**
@@ -171,17 +127,19 @@
      * {@link MbmsException#ERROR_END_OF_SESSION}
      */
     public void getStreamingServices(List<String> classList) throws MbmsException {
-        if (mService == null) {
+        IMbmsStreamingService streamingService = mService.get();
+        if (streamingService == null) {
             throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
         }
         try {
-            int returnCode = mService.getStreamingServices(mAppName, mSubscriptionId, classList);
+            int returnCode = streamingService.getStreamingServices(
+                    mAppName, mSubscriptionId, classList);
             if (returnCode != MbmsException.SUCCESS) {
                 throw new MbmsException(returnCode);
             }
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
-            mService = null;
+            mService.set(null);
             throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
         }
     }
@@ -190,7 +148,7 @@
      * Starts streaming a requested service, reporting status to the indicated listener.
      * Returns an object used to control that stream. The stream may not be ready for consumption
      * immediately upon return from this method -- wait until the streaming state has been
-     * reported via {@link android.telephony.mbms.StreamingServiceCallback#streamStateChanged(int)}.
+     * reported via {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int)}
      *
      * May throw an {@link MbmsException} containing any of the following error codes:
      * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
@@ -203,71 +161,47 @@
      */
     public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
             StreamingServiceCallback listener) throws MbmsException {
-        if (mService == null) {
+        IMbmsStreamingService streamingService = mService.get();
+        if (streamingService == null) {
             throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
         }
 
         try {
-            int returnCode = mService.startStreaming(
+            int returnCode = streamingService.startStreaming(
                     mAppName, mSubscriptionId, serviceInfo.getServiceId(), listener);
             if (returnCode != MbmsException.SUCCESS) {
                 throw new MbmsException(returnCode);
             }
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
-            mService = null;
+            mService.set(null);
             throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
         }
 
         return new StreamingService(
-                mAppName, mSubscriptionId, mService, serviceInfo, listener);
+                mAppName, mSubscriptionId, streamingService, serviceInfo, listener);
     }
 
     private void bindAndInitialize() throws MbmsException {
-        // Kick off the binding, and synchronously wait until binding is complete
-        final CountDownLatch latch = new CountDownLatch(1);
-        ServiceListener bindListener = new ServiceListener() {
-            @Override
-            public void onServiceConnected() {
-                latch.countDown();
-            }
+        MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
+                new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IMbmsStreamingService streamingService =
+                                IMbmsStreamingService.Stub.asInterface(service);
+                        try {
+                            streamingService.initialize(mCallbackToApp, mAppName, mSubscriptionId);
+                        } catch (RemoteException e) {
+                            Log.e(LOG_TAG, "Service died before initialization");
+                            return;
+                        }
+                        mService.set(null);
+                    }
 
-            @Override
-            public void onServiceDisconnected() {
-            }
-        };
-
-        synchronized (this) {
-            mServiceListeners.add(bindListener);
-        }
-
-        Intent bindIntent = new Intent();
-        bindIntent.setComponent(MbmsUtils.toComponentName(
-                MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_STREAMING_SERVICE_ACTION)));
-
-        mContext.bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
-
-        MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS);
-
-       // Remove the listener and call the initialization method through the interface.
-        synchronized (this) {
-            mServiceListeners.remove(bindListener);
-
-            if (mService == null) {
-                throw new MbmsException(MbmsException.ERROR_BIND_TIMEOUT_OR_FAILURE);
-            }
-
-            try {
-                int returnCode = mService.initialize(mCallbackToApp, mAppName, mSubscriptionId);
-                if (returnCode != MbmsException.SUCCESS) {
-                    throw new MbmsException(returnCode);
-                }
-            } catch (RemoteException e) {
-                mService = null;
-                Log.e(LOG_TAG, "Service died before initialization");
-                throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
-            }
-        }
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        mService.set(null);
+                    }
+                });
     }
-
 }
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index f3ca058..c561741 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -35,9 +35,8 @@
         private FileServiceInfo serviceInfo;
         private Uri source;
         private Uri dest;
-        private int sub;
+        private int subscriptionId;
         private String appIntent;
-        private String appName;  // not the Android app Name, the embms app Name
 
         public Builder setId(int id) {
             this.id = id;
@@ -59,8 +58,8 @@
             return this;
         }
 
-        public Builder setSub(int sub) {
-            this.sub = sub;
+        public Builder setSubscriptionId(int sub) {
+            this.subscriptionId = sub;
             return this;
         }
 
@@ -70,7 +69,8 @@
         }
 
         public DownloadRequest build() {
-            return new DownloadRequest(id, serviceInfo, source, dest, sub, appIntent, appName);
+            return new DownloadRequest(id, serviceInfo, source, dest,
+                    subscriptionId, appIntent, null);
         }
     }
 
@@ -78,7 +78,7 @@
     private final FileServiceInfo fileServiceInfo;
     private final Uri sourceUri;
     private final Uri destinationUri;
-    private final int subId;
+    private final int subscriptionId;
     private final String serializedResultIntentForApp;
     private String appName; // not the Android app Name, the embms app name
 
@@ -89,7 +89,7 @@
         fileServiceInfo = serviceInfo;
         sourceUri = source;
         destinationUri = dest;
-        subId = sub;
+        subscriptionId = sub;
         serializedResultIntentForApp = appIntent;
         appName = name;
     }
@@ -103,7 +103,7 @@
         fileServiceInfo = dr.fileServiceInfo;
         sourceUri = dr.sourceUri;
         destinationUri = dr.destinationUri;
-        subId = dr.subId;
+        subscriptionId = dr.subscriptionId;
         serializedResultIntentForApp = dr.serializedResultIntentForApp;
         appName = dr.appName;
     }
@@ -113,7 +113,7 @@
         fileServiceInfo = in.readParcelable(getClass().getClassLoader());
         sourceUri = in.readParcelable(getClass().getClassLoader());
         destinationUri = in.readParcelable(getClass().getClassLoader());
-        subId = in.readInt();
+        subscriptionId = in.readInt();
         serializedResultIntentForApp = in.readString();
         appName = in.readString();
     }
@@ -127,7 +127,7 @@
         out.writeParcelable(fileServiceInfo, flags);
         out.writeParcelable(sourceUri, flags);
         out.writeParcelable(destinationUri, flags);
-        out.writeInt(subId);
+        out.writeInt(subscriptionId);
         out.writeString(serializedResultIntentForApp);
         out.writeString(appName);
     }
@@ -148,8 +148,8 @@
         return destinationUri;
     }
 
-    public int getSubId() {
-        return subId;
+    public int getSubscriptionId() {
+        return subscriptionId;
     }
 
     public Intent getIntentForApp() {
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index d3888bd..1b87393 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -31,29 +31,22 @@
      * This is used internally but is also one of the few pieces of data about the content that is
      * exposed and may be needed for disambiguation by the application.
      */
-    final Uri uri;
+    private final Uri uri;
 
     /**
      * The mime type of the content.
      */
-    final String mimeType;
+    private final String mimeType;
 
     /**
      * The size of the file in bytes.
      */
-    final long size;
+    private final long size;
 
     /**
      * The MD5 hash of the file.
      */
-    final byte md5Hash[];
-
-    /**
-     * Gets the parent service for this file.
-     */
-    public FileServiceInfo getFileServiceInfo() {
-        return null;
-    }
+    private final byte md5Hash[];
 
     public static final Parcelable.Creator<FileInfo> CREATOR =
             new Parcelable.Creator<FileInfo>() {
@@ -68,6 +61,13 @@
         }
     };
 
+    public FileInfo(Uri uri, String mimeType, long size, byte[] md5Hash) {
+        this.uri = uri;
+        this.mimeType = mimeType;
+        this.size = size;
+        this.md5Hash = md5Hash;
+    }
+
     private FileInfo(Parcel in) {
         uri = in.readParcelable(null);
         mimeType = in.readString();
@@ -90,4 +90,20 @@
     public int describeContents() {
         return 0;
     }
+
+    public Uri getUri() {
+        return uri;
+    }
+
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public byte[] getMd5Hash() {
+        return md5Hash;
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index 8e890fd..6646dc8 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -30,13 +30,13 @@
  * @hide
  */
 public class FileServiceInfo extends ServiceInfo implements Parcelable {
-    public List<FileInfo> files;
+    private final List<FileInfo> files;
 
     public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
             List<Locale> newLocales, String newServiceId, Date start, Date end,
             List<FileInfo> newFiles) {
         super(newNames, newClassName, newLocales, newServiceId, start, end);
-        files = new ArrayList(newFiles);
+        files = new ArrayList<>(newFiles);
     }
 
     public static final Parcelable.Creator<FileServiceInfo> CREATOR =
@@ -68,4 +68,9 @@
     public int describeContents() {
         return 0;
     }
+
+    public List<FileInfo> getFiles() {
+        return files;
+    }
+
 }
diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
index 03227d0..ac2f202 100755
--- a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
@@ -24,19 +24,11 @@
  * The interface the clients top-level file download listener will satisfy.
  * @hide
  */
-interface IMbmsDownloadManagerCallback
+oneway interface IMbmsDownloadManagerCallback
 {
     void error(int errorCode, String message);
 
-    /**
-     * Called to indicate published File Services have changed.
-     *
-     * This will only be called after the application has requested
-     * a list of file services and specified a service class list
-     * of interest AND the results of a subsequent getFileServices
-     * call with the same service class list would
-     * return different
-     * results.
-     */
     void fileServicesUpdated(in List<FileServiceInfo> services);
+
+    void middlewareReady();
 }
diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
index cbf0fca..8116a7f 100755
--- a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
@@ -24,30 +24,13 @@
  * The interface the clients top-level streaming listener will satisfy.
  * @hide
  */
-interface IMbmsStreamingManagerCallback
+oneway interface IMbmsStreamingManagerCallback
 {
     void error(int errorCode, String message);
 
-    /**
-     * Called to indicate published Streaming Services have changed.
-     *
-     * This will only be called after the application has requested
-     * a list of streaming services and specified a service class list
-     * of interest AND the results of a subsequent getStreamServices
-     * call with the same service class list would
-     * return different
-     * results.
-     */
     void streamingServicesUpdated(in List<StreamingServiceInfo> services);
 
-    /**
-     * Called to indicate the active Streaming Services have changed.
-     * 
-     * This will be caused whenever a new service starts streaming or whenever
-     * MbmsStreamServiceManager.getActiveStreamingServices is called.
-     *
-     * @param services a list of StreamingServiceInfos.  May be empty if
-     *                 there are no active StreamingServices
-     */
     void activeStreamingServicesUpdated(in List<StreamingServiceInfo> services);
+
+    void middlewareReady();
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
index 16fafe4..5b22199 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
@@ -48,4 +48,17 @@
     public void fileServicesUpdated(List<FileServiceInfo> services) {
         // default implementation empty
     }
+
+    /**
+     * Called to indicate that the middleware has been initialized and is ready.
+     *
+     * Before this method is called, calling any method on an instance of
+     * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException}
+     * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+     * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}
+     */
+    @Override
+    public void middlewareReady() {
+        // default implementation empty
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index c01ddae..b51c367 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -31,8 +31,8 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -54,6 +54,11 @@
             setResultCode(1 /* TODO: define error constants */);
             return;
         }
+        if (!Objects.equals(intent.getStringExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT),
+                MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) {
+            setResultCode(1 /* TODO: define error constants */);
+            return;
+        }
 
         if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
             moveDownloadedFile(context, intent);
@@ -74,7 +79,11 @@
                 Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_INFO)) {
+            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+                Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
+                return false;
+            }
+            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FILE_INFO)) {
                 Log.w(LOG_TAG, "Download result did not include the associated file info. " +
                         "Ignoring.");
                 return false;
@@ -90,6 +99,10 @@
                 Log.w(LOG_TAG, "Temp file request not include the associated request. Ignoring.");
                 return false;
             }
+            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+                Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
+                return false;
+            }
             return true;
         }
 
@@ -121,12 +134,15 @@
         }
 
         String relativePath = calculateDestinationFileRelativePath(request,
-                (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_INFO));
+                (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO));
 
-        if (!moveTempFile(finalTempFile, destinationUri, relativePath)) {
+        Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
+        if (finalFileLocation == null) {
             Log.w(LOG_TAG, "Failed to move temp file to final destination");
+            // TODO: how do we notify the app of this?
             setResultCode(1);
         }
+        intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation);
 
         context.sendBroadcast(intentForApp);
         setResultCode(0);
@@ -226,7 +242,6 @@
         return null;
     }
 
-
     private ArrayList<UriPathPair> generateUrisForPausedFiles(Context context,
             DownloadRequest request, List<Uri> pausedFiles) {
         if (pausedFiles == null) {
@@ -258,13 +273,15 @@
 
     private static String calculateDestinationFileRelativePath(DownloadRequest request,
             FileInfo info) {
-        // TODO: determine whether this is actually the path determination scheme we want to use
-        List<String> filePathComponents = info.uri.getPathSegments();
+        List<String> filePathComponents = info.getUri().getPathSegments();
         List<String> requestPathComponents = request.getSourceUri().getPathSegments();
         Iterator<String> filePathIter = filePathComponents.iterator();
         Iterator<String> requestPathIter = requestPathComponents.iterator();
 
-        LinkedList<String> relativePathComponents = new LinkedList<>();
+        StringBuilder pathBuilder = new StringBuilder();
+        // Iterate through the segments of the carrier's URI to the file, along with the segments
+        // of the source URI specified in the download request. The relative path is calculated
+        // as the tail of the file's URI that does not match the path segments in the source URI.
         while (filePathIter.hasNext()) {
             String currFilePathComponent = filePathIter.next();
             if (requestPathIter.hasNext()) {
@@ -273,28 +290,44 @@
                     continue;
                 }
             }
-            relativePathComponents.add(currFilePathComponent);
+            pathBuilder.append(currFilePathComponent);
+            pathBuilder.append('/');
         }
-        return String.join("/", relativePathComponents);
+        // remove the trailing slash
+        if (pathBuilder.length() > 0) {
+            pathBuilder.deleteCharAt(pathBuilder.length() - 1);
+        }
+        return pathBuilder.toString();
     }
 
-    private static boolean moveTempFile(Uri fromPath, Uri toPath, String relativePath) {
+    /*
+     * Moves a tempfile located at fromPath to a new location at toPath. If
+     * toPath is a directory, the destination file will be located at  relativePath
+     * underneath toPath.
+     */
+    private static Uri moveTempFile(Uri fromPath, Uri toPath, String relativePath) {
         if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
             Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
-            return false;
+            return null;
         }
         if (!ContentResolver.SCHEME_FILE.equals(toPath.getScheme())) {
             Log.w(LOG_TAG, "Moving destination uri " + toPath + " does not have a file scheme");
-            return false;
+            return null;
         }
 
         File fromFile = new File(fromPath.getSchemeSpecificPart());
-        File toFile = new File(toPath.getSchemeSpecificPart(), relativePath);
+        File toFile = new File(toPath.getSchemeSpecificPart());
+        if (toFile.isDirectory()) {
+            toFile = new File(toFile, relativePath);
+        }
         toFile.getParentFile().mkdirs();
 
-        // TODO: This may not work if the two files are on different filesystems. Should we
-        // enforce that the temp file storage and the permanent storage are both in the same fs?
-        return fromFile.renameTo(toFile);
+        // TODO: This will not work if the two files are on different filesystems. Add manual
+        // copy later.
+        if (fromFile.renameTo(toFile)) {
+            return Uri.fromFile(toFile);
+        }
+        return null;
     }
 
     private static boolean verifyTempFilePath(Context context, DownloadRequest request,
@@ -323,8 +356,7 @@
      * Returns a File linked to the directory used to store temp files for this request
      */
     private static File getEmbmsTempFileDirForRequest(Context context, DownloadRequest request) {
-        File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(
-                context, getFileProviderAuthority(context));
+        File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
 
         // TODO: better naming scheme for temp file dirs
         String tempFileDirName = String.valueOf(request.getFileServiceInfo().getServiceId());
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index 6b90592..8260b72 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -22,7 +22,7 @@
     public static final int ERROR_NO_SERVICE_INSTALLED = 1;
     public static final int ERROR_MULTIPLE_SERVICES_INSTALLED = 2;
     public static final int ERROR_BIND_TIMEOUT_OR_FAILURE = 3;
-    public static final int ERROR_UNABLE_TO_INITIALIZE = 4;
+    public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 4;
     public static final int ERROR_ALREADY_INITIALIZED = 5;
     public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 6;
     public static final int ERROR_MIDDLEWARE_NOT_BOUND = 7;
@@ -36,6 +36,7 @@
     public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 15;
     public static final int ERROR_UNABLE_TO_READ_SIM = 16;
     public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 17;
+    public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 18;
 
     private final int mErrorCode;
 
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
index b3bc814..27d9878 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
@@ -61,4 +61,17 @@
     public void activeStreamingServicesUpdated(List<StreamingServiceInfo> services) {
         // default implementation empty
     }
+
+    /**
+     * Called to indicate that the middleware has been initialized and is ready.
+     *
+     * Before this method is called, calling any method on an instance of
+     * {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException}
+     * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+     * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}
+     */
+    @Override
+    public void middlewareReady() {
+        // default implementation empty
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
index 9842581..c4d033b 100644
--- a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
+++ b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
@@ -22,6 +22,7 @@
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.database.Cursor;
@@ -32,14 +33,15 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.Objects;
 
 /**
  * @hide
  */
 public class MbmsTempFileProvider extends ContentProvider {
-    public static final String META_DATA_USE_EXTERNAL_STORAGE = "use-external-storage";
-    public static final String META_DATA_TEMP_FILE_DIRECTORY = "temp-file-path";
     public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+    public static final String TEMP_FILE_ROOT_PREF_FILE_NAME = "MbmsTempFileRootPrefs";
+    public static final String TEMP_FILE_ROOT_PREF_NAME = "mbms_temp_file_root";
 
     private String mAuthority;
     private Context mContext;
@@ -114,7 +116,7 @@
 
         // Make sure the temp file is contained in the temp file directory as configured in the
         // manifest
-        File tempFileDir = getEmbmsTempFileDir(context, authority);
+        File tempFileDir = getEmbmsTempFileDir(context);
         if (!MbmsUtils.isContainedIn(tempFileDir, file)) {
             throw new IllegalArgumentException("File " + file + " is not contained in the temp " +
                     "file directory, which is " + tempFileDir);
@@ -147,13 +149,17 @@
         if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
             throw new IllegalArgumentException("Uri must have scheme content");
         }
+        if (!Objects.equals(authority, uri.getAuthority())) {
+            throw new IllegalArgumentException("Uri does not have a matching authority: " +
+                    authority + ", " + uri.getAuthority());
+        }
 
         String relPath = Uri.decode(uri.getEncodedPath());
         File file;
         File tempFileDir;
 
         try {
-            tempFileDir = getEmbmsTempFileDir(context, authority).getCanonicalFile();
+            tempFileDir = getEmbmsTempFileDir(context).getCanonicalFile();
             file = new File(tempFileDir, relPath).getCanonicalFile();
         } catch (IOException e) {
             throw new FileNotFoundException("Could not resolve paths");
@@ -169,25 +175,18 @@
     /**
      * Returns a File for the directory used to store temp files for this app
      */
-    public static File getEmbmsTempFileDir(Context context, String authority) {
-        Bundle metadata = getMetadata(context, authority);
-        File parentDirectory;
-        if (metadata.getBoolean(META_DATA_USE_EXTERNAL_STORAGE, false)) {
-            parentDirectory = context.getExternalFilesDir(null);
-        } else {
-            parentDirectory = context.getFilesDir();
+    public static File getEmbmsTempFileDir(Context context) {
+        SharedPreferences prefs = context.getSharedPreferences(TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        String storedTempFileRoot = prefs.getString(TEMP_FILE_ROOT_PREF_NAME, null);
+        try {
+            if (storedTempFileRoot != null) {
+                return new File(storedTempFileRoot).getCanonicalFile();
+            } else {
+                return new File(context.getFilesDir(), DEFAULT_TOP_LEVEL_TEMP_DIRECTORY)
+                        .getCanonicalFile();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to canonicalize temp file root path " + e);
         }
-
-        String tmpFilePath = metadata.getString(META_DATA_TEMP_FILE_DIRECTORY);
-        if (tmpFilePath == null) {
-            tmpFilePath = DEFAULT_TOP_LEVEL_TEMP_DIRECTORY;
-        }
-        return new File(parentDirectory, tmpFilePath);
-    }
-
-    private static Bundle getMetadata(Context context, String authority) {
-        final ProviderInfo info = context.getPackageManager()
-                .resolveContentProvider(authority, PackageManager.GET_META_DATA);
-        return info.metaData;
     }
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index de30805..7d47275 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.*;
 import android.content.pm.ServiceInfo;
 import android.telephony.MbmsDownloadManager;
@@ -46,20 +47,6 @@
         }
     }
 
-    public static void waitOnLatchWithTimeout(CountDownLatch l, long timeoutMs) {
-        long endTime = System.currentTimeMillis() + timeoutMs;
-        while (System.currentTimeMillis() < endTime) {
-            try {
-                l.await(timeoutMs, TimeUnit.MILLISECONDS);
-            } catch (InterruptedException e) {
-                // keep waiting
-            }
-            if (l.getCount() <= 0) {
-                return;
-            }
-        }
-    }
-
     public static ComponentName toComponentName(ComponentInfo ci) {
         return new ComponentName(ci.packageName, ci.name);
     }
@@ -83,4 +70,19 @@
         }
         return downloadServices.get(0).serviceInfo;
     }
+
+    public static void startBinding(Context context, String serviceAction,
+            ServiceConnection serviceConnection) throws MbmsException {
+        Intent bindIntent = new Intent();
+        ServiceInfo mbmsServiceInfo =
+                MbmsUtils.getMiddlewareServiceInfo(context, serviceAction);
+
+        if (mbmsServiceInfo == null) {
+            throw new MbmsException(MbmsException.ERROR_NO_SERVICE_INSTALLED);
+        }
+
+        bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo));
+
+        context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index 6c2b816..ff7d233 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -47,6 +47,7 @@
      */
     int getFileServices(String appName, int subId, in List<String> serviceClasses);
 
+    int setTempFileRootDirectory(String appName, int subId, String rootDirectoryPath);
     /**
      * should move the params into a DownloadRequest parcelable
      */
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 505aeae..9577dd2 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -32,13 +32,19 @@
  */
 public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
     @Override
-    public void initialize(String appName, int subId, IMbmsDownloadManagerCallback listener)
-            throws RemoteException {
+    public void initialize(String appName, int subscriptionId,
+            IMbmsDownloadManagerCallback listener) throws RemoteException {
     }
 
     @Override
-    public int getFileServices(String appName, int subId, List<String> serviceClasses) throws
-            RemoteException {
+    public int getFileServices(String appName, int subscriptionId, List<String> serviceClasses)
+            throws RemoteException {
+        return 0;
+    }
+
+    @Override
+    public int setTempFileRootDirectory(String appName, int subscriptionId,
+            String rootDirectoryPath) throws RemoteException {
         return 0;
     }
 
diff --git a/test-runner/src/android/test/DatabaseTestUtils.java b/test-runner/src/android/test/DatabaseTestUtils.java
deleted file mode 100644
index 1980d92..0000000
--- a/test-runner/src/android/test/DatabaseTestUtils.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2008 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.test;
-
-import android.database.sqlite.SQLiteDatabase;
-import android.database.Cursor;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * A collection of utilities for writing unit tests for database code.
- * @hide pending API council approval
- */
-@Deprecated
-public class DatabaseTestUtils {
-
-    /**
-     * Compares the schema of two databases and asserts that they are equal.
-     * @param expectedDb the db that is known to have the correct schema
-     * @param db the db whose schema should be checked
-     */
-    public static void assertSchemaEquals(SQLiteDatabase expectedDb, SQLiteDatabase db) {
-        Set<String> expectedSchema = getSchemaSet(expectedDb);
-        Set<String> schema = getSchemaSet(db);
-        MoreAsserts.assertEquals(expectedSchema, schema);
-    }
-
-    private static Set<String> getSchemaSet(SQLiteDatabase db) {
-        Set<String> schemaSet = new HashSet<>();
-
-        Cursor entityCursor = db.rawQuery("SELECT sql FROM sqlite_master", null);
-        try {
-            while (entityCursor.moveToNext()) {
-                String sql = entityCursor.getString(0);
-                schemaSet.add(sql);
-            }
-        } finally {
-            entityCursor.close();
-        }
-        return schemaSet;
-    }
-}
diff --git a/test-runner/src/android/test/InstrumentationCoreTestRunner.java b/test-runner/src/android/test/InstrumentationCoreTestRunner.java
deleted file mode 100644
index 2b05e4a..0000000
--- a/test-runner/src/android/test/InstrumentationCoreTestRunner.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2008 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.test;
-
-import java.io.File;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.List;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestListener;
-import android.os.Bundle;
-import android.test.suitebuilder.TestMethod;
-import android.test.suitebuilder.annotation.HasAnnotation;
-import android.util.Log;
-
-/**
- * This test runner extends the default InstrumentationTestRunner. It overrides
- * the {@code onCreate(Bundle)} method and sets the system properties necessary
- * for many core tests to run. This is needed because there are some core tests
- * that need writing access to the file system. We also need to set the harness
- * Thread's context ClassLoader. Otherwise some classes and resources will not
- * be found. Finally, we add a means to free memory allocated by a TestCase
- * after its execution.
- *
- * @hide
- */
-@Deprecated
-public class InstrumentationCoreTestRunner extends InstrumentationTestRunner {
-
-    /**
-     * Convenience definition of our log tag.
-     */
-    private static final String TAG = "InstrumentationCoreTestRunner";
-
-    /**
-     * True if (and only if) we are running in single-test mode (as opposed to
-     * batch mode).
-     */
-    private boolean singleTest = false;
-
-    @Override
-    public void onCreate(Bundle arguments) {
-        // We might want to move this to /sdcard, if is is mounted/writable.
-        File cacheDir = getTargetContext().getCacheDir();
-
-        // Set some properties that the core tests absolutely need.
-        System.setProperty("user.language", "en");
-        System.setProperty("user.region", "US");
-
-        System.setProperty("java.home", cacheDir.getAbsolutePath());
-        System.setProperty("user.home", cacheDir.getAbsolutePath());
-        System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
-
-        if (arguments != null) {
-            String classArg = arguments.getString(ARGUMENT_TEST_CLASS);
-            singleTest = classArg != null && classArg.contains("#");
-        }
-
-        super.onCreate(arguments);
-    }
-
-    @Override
-    protected AndroidTestRunner getAndroidTestRunner() {
-        AndroidTestRunner runner = super.getAndroidTestRunner();
-
-        runner.addTestListener(new TestListener() {
-            /**
-             * The last test class we executed code from.
-             */
-            private Class<?> lastClass;
-
-            /**
-             * The minimum time we expect a test to take.
-             */
-            private static final int MINIMUM_TIME = 100;
-
-            /**
-             * The start time of our current test in System.currentTimeMillis().
-             */
-            private long startTime;
-
-            public void startTest(Test test) {
-                if (test.getClass() != lastClass) {
-                    lastClass = test.getClass();
-                    printMemory(test.getClass());
-                }
-
-                Thread.currentThread().setContextClassLoader(
-                        test.getClass().getClassLoader());
-
-                startTime = System.currentTimeMillis();
-            }
-
-            public void endTest(Test test) {
-                if (test instanceof TestCase) {
-                    cleanup((TestCase)test);
-
-                    /*
-                     * Make sure all tests take at least MINIMUM_TIME to
-                     * complete. If they don't, we wait a bit. The Cupcake
-                     * Binder can't handle too many operations in a very
-                     * short time, which causes headache for the CTS.
-                     */
-                    long timeTaken = System.currentTimeMillis() - startTime;
-
-                    if (timeTaken < MINIMUM_TIME) {
-                        try {
-                            Thread.sleep(MINIMUM_TIME - timeTaken);
-                        } catch (InterruptedException ignored) {
-                            // We don't care.
-                        }
-                    }
-                }
-            }
-
-            public void addError(Test test, Throwable t) {
-                // This space intentionally left blank.
-            }
-
-            public void addFailure(Test test, AssertionFailedError t) {
-                // This space intentionally left blank.
-            }
-
-            /**
-             * Dumps some memory info.
-             */
-            private void printMemory(Class<? extends Test> testClass) {
-                Runtime runtime = Runtime.getRuntime();
-
-                long total = runtime.totalMemory();
-                long free = runtime.freeMemory();
-                long used = total - free;
-
-                Log.d(TAG, "Total memory  : " + total);
-                Log.d(TAG, "Used memory   : " + used);
-                Log.d(TAG, "Free memory   : " + free);
-                Log.d(TAG, "Now executing : " + testClass.getName());
-            }
-
-            /**
-             * Nulls all non-static reference fields in the given test class.
-             * This method helps us with those test classes that don't have an
-             * explicit tearDown() method. Normally the garbage collector should
-             * take care of everything, but since JUnit keeps references to all
-             * test cases, a little help might be a good idea.
-             */
-            private void cleanup(TestCase test) {
-                Class<?> clazz = test.getClass();
-
-                while (clazz != TestCase.class) {
-                    Field[] fields = clazz.getDeclaredFields();
-                    for (int i = 0; i < fields.length; i++) {
-                        Field f = fields[i];
-                        if (!f.getType().isPrimitive() &&
-                                !Modifier.isStatic(f.getModifiers())) {
-                            try {
-                                f.setAccessible(true);
-                                f.set(test, null);
-                            } catch (Exception ignored) {
-                                // Nothing we can do about it.
-                            }
-                        }
-                    }
-
-                    clazz = clazz.getSuperclass();
-                }
-            }
-
-        });
-
-        return runner;
-    }
-}
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index 9bd4c96..6e5492b 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -178,13 +178,13 @@
 public class InstrumentationTestRunner extends Instrumentation implements TestSuiteProvider {
 
     /** @hide */
-    public static final String ARGUMENT_TEST_CLASS = "class";
+    static final String ARGUMENT_TEST_CLASS = "class";
     /** @hide */
-    public static final String ARGUMENT_TEST_PACKAGE = "package";
+    private static final String ARGUMENT_TEST_PACKAGE = "package";
     /** @hide */
-    public static final String ARGUMENT_TEST_SIZE_PREDICATE = "size";
+    private static final String ARGUMENT_TEST_SIZE_PREDICATE = "size";
     /** @hide */
-    public static final String ARGUMENT_DELAY_MSEC = "delay_msec";
+    static final String ARGUMENT_DELAY_MSEC = "delay_msec";
 
     private static final String SMALL_SUITE = "small";
     private static final String MEDIUM_SUITE = "medium";
@@ -208,7 +208,7 @@
      */
     private static final float MEDIUM_SUITE_MAX_RUNTIME = 1000;
 
-    /**
+    /*
      * The following keys are used in the status bundle to provide structured reports to
      * an IInstrumentationWatcher.
      */
@@ -476,7 +476,7 @@
 
    /**
     * Returns the test predicate object, corresponding to the annotation class value provided via
-    * the {@link ARGUMENT_ANNOTATION} argument.
+    * the {@link #ARGUMENT_ANNOTATION} argument.
     *
     * @return the predicate or <code>null</code>
     */
@@ -490,7 +490,7 @@
 
     /**
      * Returns the negative test predicate object, corresponding to the annotation class value
-     * provided via the {@link ARGUMENT_NOT_ANNOTATION} argument.
+     * provided via the {@link #ARGUMENT_NOT_ANNOTATION} argument.
      *
      * @return the predicate or <code>null</code>
      */
diff --git a/test-runner/src/android/test/InstrumentationUtils.java b/test-runner/src/android/test/InstrumentationUtils.java
deleted file mode 100644
index cc50813..0000000
--- a/test-runner/src/android/test/InstrumentationUtils.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2007 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.test;
-
-/**
- * The InstrumentationUtils class has all the utility functions needed for
- * instrumentation tests.
- *
- * {@hide} - Not currently used.
- */
-@Deprecated
-public class InstrumentationUtils {
-    /**
-     * An utility function that returns the menu identifier for a particular
-     * menu item.
-     *
-     * @param cls Class object of the class that handles the menu ite,.
-     * @param identifier Menu identifier.
-     * @return The integer corresponding to the menu item.
-     */
-    public static int getMenuIdentifier(Class cls, String identifier) {
-        int id = -1;
-        try {
-            Integer field = (Integer)cls.getDeclaredField(identifier).get(cls);
-            id = field.intValue();
-        } catch (NoSuchFieldException e) {
-            e.printStackTrace();
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        }
-        return id;
-    }
-
-}
diff --git a/test-runner/src/android/test/suitebuilder/InstrumentationTestSuiteBuilder.java b/test-runner/src/android/test/suitebuilder/InstrumentationTestSuiteBuilder.java
deleted file mode 100644
index 128396e..0000000
--- a/test-runner/src/android/test/suitebuilder/InstrumentationTestSuiteBuilder.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2008 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.test.suitebuilder;
-
-/**
- * A suite builder that finds instrumentation tests.
- * 
- * {@hide} Not needed for 1.0 SDK.
- */
-public class InstrumentationTestSuiteBuilder extends TestSuiteBuilder {
-
-    public InstrumentationTestSuiteBuilder(Class clazz) {
-        this(clazz.getName(), clazz.getClassLoader());
-    }
-
-
-    public InstrumentationTestSuiteBuilder(String name, ClassLoader classLoader) {
-        super(name, classLoader);
-        addRequirements(TestPredicates.SELECT_INSTRUMENTATION);
-    }
-}
diff --git a/test-runner/src/android/test/suitebuilder/TestGrouping.java b/test-runner/src/android/test/suitebuilder/TestGrouping.java
index a2b94ff..307afb5 100644
--- a/test-runner/src/android/test/suitebuilder/TestGrouping.java
+++ b/test-runner/src/android/test/suitebuilder/TestGrouping.java
@@ -44,23 +44,23 @@
  * 
  * {@hide} Not needed for 1.0 SDK.
  */
-public class TestGrouping {
+class TestGrouping {
 
     private static final String LOG_TAG = "TestGrouping";
 
-    SortedSet<Class<? extends TestCase>> testCaseClasses;
+    private final SortedSet<Class<? extends TestCase>> testCaseClasses;
 
-    public static final Comparator<Class<? extends TestCase>> SORT_BY_SIMPLE_NAME
+    static final Comparator<Class<? extends TestCase>> SORT_BY_SIMPLE_NAME
             = new SortBySimpleName();
 
-    public static final Comparator<Class<? extends TestCase>> SORT_BY_FULLY_QUALIFIED_NAME
+    static final Comparator<Class<? extends TestCase>> SORT_BY_FULLY_QUALIFIED_NAME
             = new SortByFullyQualifiedName();
 
-    protected String firstIncludedPackage = null;
-    private ClassLoader classLoader;
+    private final ClassLoader classLoader;
 
-    public TestGrouping(Comparator<Class<? extends TestCase>> comparator) {
+    TestGrouping(Comparator<Class<? extends TestCase>> comparator, ClassLoader classLoader) {
         testCaseClasses = new TreeSet<Class<? extends TestCase>>(comparator);
+        this.classLoader = classLoader;
     }
 
     /**
@@ -77,15 +77,11 @@
         return testMethods;
     }
 
-    protected List<Method> getTestMethods(Class<? extends TestCase> testCaseClass) {
+    private List<Method> getTestMethods(Class<? extends TestCase> testCaseClass) {
         List<Method> methods = Arrays.asList(testCaseClass.getMethods());
         return select(methods, new TestMethodPredicate());
     }
 
-    SortedSet<Class<? extends TestCase>> getTestCaseClasses() {
-        return testCaseClasses;
-    }
-
     public boolean equals(Object o) {
         if (this == o) {
             return true;
@@ -110,9 +106,8 @@
      * or in a sub-package.
      *
      * @param packageNames Names of packages to add.
-     * @return The {@link TestGrouping} for method chaining.
      */
-    public TestGrouping addPackagesRecursive(String... packageNames) {
+    void addPackagesRecursive(String... packageNames) {
         for (String packageName : packageNames) {
             List<Class<? extends TestCase>> addedClasses = testCaseClassesInPackage(packageName);
             if (addedClasses.isEmpty()) {
@@ -120,11 +115,7 @@
                         + "' could not be found or has no tests");
             }
             testCaseClasses.addAll(addedClasses);
-            if (firstIncludedPackage == null) {
-                firstIncludedPackage = packageName;
-            }
         }
-        return this;
     }
 
     /**
@@ -132,21 +123,11 @@
      * specified.
      *
      * @param packageNames Names of packages to remove.
-     * @return The {@link TestGrouping} for method chaining.
      */
-    public TestGrouping removePackagesRecursive(String... packageNames) {
+    void removePackagesRecursive(String... packageNames) {
         for (String packageName : packageNames) {
             testCaseClasses.removeAll(testCaseClassesInPackage(packageName));
         }
-        return this;
-    }
-
-    /**
-     * @return The first package name passed to {@link #addPackagesRecursive(String[])}, or null
-     *         if that method was never called.
-     */
-    public String getFirstIncludedPackage() {
-        return firstIncludedPackage;
     }
 
     private List<Class<? extends TestCase>> testCaseClassesInPackage(String packageName) {
@@ -176,10 +157,6 @@
         return selectedItems;
     }
 
-    public void setClassLoader(ClassLoader classLoader) {
-        this.classLoader = classLoader;
-    }
-
     /**
      * Sort classes by their simple names (i.e. without the package prefix), using
      * their packages to sort classes with the same name.
diff --git a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
index cf6936b..6158e0c 100644
--- a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
+++ b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
@@ -44,8 +44,7 @@
 @Deprecated
 public class TestSuiteBuilder {
 
-    private Context context;
-    private final TestGrouping testGrouping = new TestGrouping(SORT_BY_FULLY_QUALIFIED_NAME);
+    private final TestGrouping testGrouping;
     private final Set<Predicate<TestMethod>> predicates = new HashSet<Predicate<TestMethod>>();
     private List<TestCase> testCases;
     private TestSuite rootSuite;
@@ -67,7 +66,7 @@
 
     public TestSuiteBuilder(String name, ClassLoader classLoader) {
         this.suiteName = name;
-        this.testGrouping.setClassLoader(classLoader);
+        this.testGrouping = new TestGrouping(SORT_BY_FULLY_QUALIFIED_NAME, classLoader);
         this.testCases = new ArrayList<>();
         addRequirements(REJECT_SUPPRESSED);
     }
@@ -244,15 +243,6 @@
         }
     }
 
-    /**
-     * @return the test package that represents the packages that were included for our test suite.
-     *
-     * {@hide} Not needed for 1.0 SDK.
-     */
-    protected TestGrouping getTestGrouping() {
-        return testGrouping;
-    }
-
     private boolean satisfiesAllPredicates(TestMethod test) {
         for (Predicate<TestMethod> predicate : predicates) {
             if (!predicate.apply(test)) {
diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk
index 68fd662..cc9b01d 100644
--- a/test-runner/tests/Android.mk
+++ b/test-runner/tests/Android.mk
@@ -16,6 +16,13 @@
 include $(CLEAR_VARS)
 
 # We only want this apk build for tests.
+#
+# Run the tests using the following commands:
+#   adb -r install ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk
+#   adb shell am instrument \
+        -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \
+        -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner
+#
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/test-runner/tests/src/android/test/TestCaseUtilTest.java b/test-runner/tests/src/android/test/TestCaseUtilTest.java
index bc6fa92..9d12eaf 100644
--- a/test-runner/tests/src/android/test/TestCaseUtilTest.java
+++ b/test-runner/tests/src/android/test/TestCaseUtilTest.java
@@ -29,9 +29,7 @@
 
         List<String> testCaseNames = TestCaseUtil.getTestCaseNames(testSuite, false);
 
-        assertEquals(2, testCaseNames.size());
-        assertTrue(testCaseNames.get(0).endsWith("OneTestTestCase"));
-        assertTrue(testCaseNames.get(1).endsWith("OneTestTestSuite"));
+        assertEquals(0, testCaseNames.size());
     }
     
     public void testGetTestCaseNamesForTestCaseWithSuiteMethod() throws Exception {
diff --git a/test-runner/tests/src/android/test/suitebuilder/InstrumentationTestSuiteBuilderTest.java b/test-runner/tests/src/android/test/suitebuilder/InstrumentationTestSuiteBuilderTest.java
deleted file mode 100644
index 1872803..0000000
--- a/test-runner/tests/src/android/test/suitebuilder/InstrumentationTestSuiteBuilderTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2008 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.test.suitebuilder;
-
-import static android.test.suitebuilder.ListTestCaseNames.getTestCaseNames;
-import android.test.suitebuilder.examples.OuterTest;
-import android.test.suitebuilder.examples.instrumentation.InstrumentationTest;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestListener;
-import junit.framework.TestResult;
-import junit.framework.TestSuite;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class InstrumentationTestSuiteBuilderTest extends TestCase {
-
-    private InstrumentationTestSuiteBuilder instrumentationTestSuiteBuilder;
-
-    protected void setUp() throws Exception {
-        super.setUp();
-        instrumentationTestSuiteBuilder = new InstrumentationTestSuiteBuilder(getClass());
-    }
-
-    public void testShouldIncludeIntrumentationTests() throws Exception {
-        instrumentationTestSuiteBuilder.includePackages(packageFor(InstrumentationTest.class));
-
-        SuiteExecutionRecorder recorder = runSuite(instrumentationTestSuiteBuilder);
-
-        assertEquals(1, recorder.testsSeen.size());
-        assertTrue(recorder.saw("InstrumentationTest.testInstrumentation"));
-    }
-
-    public void testShouldOnlyIncludeIntrumentationTests() throws Exception {
-        TestSuite testSuite = new OuterTest()
-                .buildTestsUnderHereWith(instrumentationTestSuiteBuilder);
-        List<String> testCaseNames = getTestCaseNames(testSuite);
-        assertEquals(1, testCaseNames.size());
-        assertEquals("testInstrumentation", testCaseNames.get(0));
-    }
-
-    private static String packageFor(Class clazz) {
-        String className = clazz.getName();
-        return className.substring(0, className.lastIndexOf('.'));
-    }
-
-    private SuiteExecutionRecorder runSuite(TestSuiteBuilder builder) {
-        TestSuite suite = builder.build();
-        SuiteExecutionRecorder recorder = new SuiteExecutionRecorder();
-        TestResult result = new TestResult();
-        result.addListener(recorder);
-        suite.run(result);
-        return recorder;
-    }
-
-    private class SuiteExecutionRecorder implements TestListener {
-
-        private Set<String> failures = new HashSet<String>();
-        private Set<String> errors = new HashSet<String>();
-        private Set<String> testsSeen = new HashSet<String>();
-
-        public void addError(Test test, Throwable t) {
-            errors.add(testName(test));
-        }
-
-        public void addFailure(Test test, AssertionFailedError t) {
-            failures.add(testName(test));
-        }
-
-        public void endTest(Test test) {
-        }
-
-        public void startTest(Test test) {
-            testsSeen.add(testName(test));
-        }
-
-        public boolean saw(String testName) {
-            return testsSeen.contains(testName);
-        }
-
-        public boolean failed(String testName) {
-            return failures.contains(testName);
-        }
-
-        public boolean errored(String testName) {
-            return errors.contains(testName);
-        }
-
-        public boolean passed(String testName) {
-            return saw(testName) && !failed(testName) && !errored(testName);
-        }
-
-        private String testName(Test test) {
-            TestCase testCase = (TestCase) test;
-            return testCase.getClass().getSimpleName() + "." + testCase.getName();
-        }
-    }
-}
diff --git a/test-runner/tests/src/android/test/suitebuilder/TestGroupingTest.java b/test-runner/tests/src/android/test/suitebuilder/TestGroupingTest.java
index f4477d1..972bfb4 100644
--- a/test-runner/tests/src/android/test/suitebuilder/TestGroupingTest.java
+++ b/test-runner/tests/src/android/test/suitebuilder/TestGroupingTest.java
@@ -30,7 +30,7 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mGrouping = new TestGrouping(TestGrouping.SORT_BY_SIMPLE_NAME);
+        mGrouping = new TestGrouping(TestGrouping.SORT_BY_SIMPLE_NAME, getClass().getClassLoader());
     }
 
     /**
diff --git a/test-runner/tests/src/android/test/suitebuilder/TestSuiteBuilderTest.java b/test-runner/tests/src/android/test/suitebuilder/TestSuiteBuilderTest.java
index 293c813..a2e51a1 100644
--- a/test-runner/tests/src/android/test/suitebuilder/TestSuiteBuilderTest.java
+++ b/test-runner/tests/src/android/test/suitebuilder/TestSuiteBuilderTest.java
@@ -135,10 +135,23 @@
 
         TestSuite testSuite = new OuterTest().buildTestsUnderHereRecursively();
         assertContentsInOrder(getTestCaseNames(testSuite),
-                "testOuter", "testErrorOne", "testErrorTwo", "testFailOne", "testFailTwo",
-                "testInstrumentation", "testLevel1", "testLevel2", "testAnotherOne",
-                "testSimpleOne", "testSimpleTwo", "testNonSmoke", "testSmoke", "testSubclass",
-                "testSuperclass", "testUnSuppressedMethod");
+                "testOuter",
+                "testPublicConstructor",
+                "testErrorOne",
+                "testErrorTwo",
+                "testFailOne",
+                "testFailTwo",
+                "testInstrumentation",
+                "testLevel1",
+                "testLevel2",
+                "testAnotherOne",
+                "testSimpleOne",
+                "testSimpleTwo",
+                "testNonSmoke",
+                "testSmoke",
+                "testSubclass",
+                "testSuperclass",
+                "testUnSuppressedMethod");
     }
 
     private void assertContentsInOrder(List<String> actual, String... source) {
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/error/ErrorTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/error/ErrorTest.java
index f1f6113..ddf5dd1 100644
--- a/test-runner/tests/src/android/test/suitebuilder/examples/error/ErrorTest.java
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/error/ErrorTest.java
@@ -18,6 +18,7 @@
 
 import junit.framework.TestCase;
 
+@RunAsPartOfSeparateTest
 public class ErrorTest extends TestCase {
 
     public void testErrorOne() throws Exception {
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/error/FailingTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/error/FailingTest.java
index 428fd23..0170b2f 100644
--- a/test-runner/tests/src/android/test/suitebuilder/examples/error/FailingTest.java
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/error/FailingTest.java
@@ -18,6 +18,7 @@
 
 import junit.framework.TestCase;
 
+@RunAsPartOfSeparateTest
 public class FailingTest extends TestCase {
 
     public void testFailOne() throws Exception {
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/error/RunAsPartOfSeparateTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/error/RunAsPartOfSeparateTest.java
new file mode 100644
index 0000000..2b3a252
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/error/RunAsPartOfSeparateTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.test.suitebuilder.examples.error;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that marks tests that should only be run as part of a separate test and not on their
+ * own.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface RunAsPartOfSeparateTest {
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index be0924a..5b4e901 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -264,13 +264,16 @@
         mIntents.remove(bcast);
     }
 
-    public void workingLocalOnlyHotspot(boolean enrichedApBroadcast) throws Exception {
+    public void workingLocalOnlyHotspot(
+            boolean withInterfaceStateChanged, boolean enrichedApBroadcast) throws Exception {
         when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
 
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // hotspot mode is to be started.
-        mTethering.interfaceStatusChanged(mTestIfname, true);
+        if (withInterfaceStateChanged) {
+            mTethering.interfaceStatusChanged(mTestIfname, true);
+        }
         if (enrichedApBroadcast) {
             sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY);
         } else {
@@ -320,12 +323,17 @@
 
     @Test
     public void workingLocalOnlyHotspotLegacyApBroadcast() throws Exception {
-        workingLocalOnlyHotspot(false);
+        workingLocalOnlyHotspot(true, false);
     }
 
     @Test
     public void workingLocalOnlyHotspotEnrichedApBroadcast() throws Exception {
-        workingLocalOnlyHotspot(true);
+        workingLocalOnlyHotspot(true, true);
+    }
+
+    @Test
+    public void workingLocalOnlyHotspotEnrichedApBroadcastWithoutInterfaceUp() throws Exception {
+        workingLocalOnlyHotspot(false, true);
     }
 
     public void workingWifiTethering(boolean enrichedApBroadcast) throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 4d340d1..1ddaf66 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -62,7 +62,8 @@
     @Mock private OffloadHardwareInterface mHardware;
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
-    final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class);
+    private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
+            ArgumentCaptor.forClass(ArrayList.class);
     private MockContentResolver mContentResolver;
 
     @Before public void setUp() throws Exception {
@@ -155,8 +156,7 @@
         lp.setInterfaceName(testIfName);
         offload.setUpstreamLinkProperties(lp);
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
-                eq(testIfName), eq(null), eq(null), mStringArrayCaptor.capture());
-        assertTrue(mStringArrayCaptor.getValue().isEmpty());
+                eq(testIfName), eq(null), eq(null), eq(null));
         inOrder.verifyNoMoreInteractions();
 
         final String ipv4Addr = "192.0.2.5";
@@ -164,16 +164,14 @@
         lp.addLinkAddress(new LinkAddress(linkAddr));
         offload.setUpstreamLinkProperties(lp);
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
-                eq(testIfName), eq(ipv4Addr), eq(null), mStringArrayCaptor.capture());
-        assertTrue(mStringArrayCaptor.getValue().isEmpty());
+                eq(testIfName), eq(ipv4Addr), eq(null), eq(null));
         inOrder.verifyNoMoreInteractions();
 
         final String ipv4Gateway = "192.0.2.1";
         lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway)));
         offload.setUpstreamLinkProperties(lp);
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
-                eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
-        assertTrue(mStringArrayCaptor.getValue().isEmpty());
+                eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null));
         inOrder.verifyNoMoreInteractions();
 
         final String ipv6Gw1 = "fe80::cafe";
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index ce419a5..db5373a 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.tethering;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -40,17 +42,23 @@
 import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.RouteInfo;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+
+import java.net.Inet4Address;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -69,6 +77,8 @@
     @Mock private SharedLog mSharedLog;
 
     private final TestLooper mLooper = new TestLooper();
+    private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
+            ArgumentCaptor.forClass(LinkProperties.class);
     private TetherInterfaceStateMachine mTestedSm;
 
     private void initStateMachine(int interfaceType) throws Exception {
@@ -77,7 +87,7 @@
                 mNMService, mStatsService, mTetherHelper);
         mTestedSm.start();
         // Starting the state machine always puts us in a consistent state and notifies
-        // the test of the world that we've changed from an unknown to available state.
+        // the rest of the world that we've changed from an unknown to available state.
         mLooper.dispatchAll();
         reset(mNMService, mStatsService, mTetherHelper);
         when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
@@ -181,7 +191,8 @@
         inOrder.verify(mTetherHelper).updateInterfaceState(
                 mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), any(LinkProperties.class));
+                eq(mTestedSm), mLinkPropertiesCaptor.capture());
+        assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
         verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
     }
 
@@ -281,7 +292,8 @@
             usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
                     mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
             usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
-                    eq(mTestedSm), any(LinkProperties.class));
+                    eq(mTestedSm), mLinkPropertiesCaptor.capture());
+            assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
         }
     }
 
@@ -298,7 +310,8 @@
         usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
                 mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
         usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), any(LinkProperties.class));
+                eq(mTestedSm), mLinkPropertiesCaptor.capture());
+        assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
     }
 
     @Test
@@ -313,7 +326,8 @@
         usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
                 mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
         usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), any(LinkProperties.class));
+                eq(mTestedSm), mLinkPropertiesCaptor.capture());
+        assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
     }
 
     @Test
@@ -360,4 +374,28 @@
                 upstreamIface);
         mLooper.dispatchAll();
     }
+
+    private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
+        // Find the first IPv4 LinkAddress.
+        LinkAddress addr4 = null;
+        for (LinkAddress addr : lp.getLinkAddresses()) {
+            if (!(addr.getAddress() instanceof Inet4Address)) continue;
+            addr4 = addr;
+            break;
+        }
+        assertTrue("missing IPv4 address", addr4 != null);
+
+        // Assert the presence of the associated directly connected route.
+        final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
+        assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
+                   lp.getRoutes().contains(directlyConnected));
+    }
+
+    private void assertNoAddressesNorRoutes(LinkProperties lp) {
+        assertTrue(lp.getLinkAddresses().isEmpty());
+        assertTrue(lp.getRoutes().isEmpty());
+        // We also check that interface name is non-empty, because we should
+        // never see an empty interface name in any LinkProperties update.
+        assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index 27be135..b68f203 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -38,6 +39,8 @@
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
 
+import java.util.Iterator;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -134,4 +137,61 @@
         assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
         assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
     }
+
+    @Test
+    public void testNoDefinedUpstreamTypesAddsEthernet() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{});
+        mHasTelephonyManager = false;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
+        // The following is because the code always adds some kind of mobile
+        // upstream, be it DUN or, in this case where we use DUN_UNSPECIFIED,
+        // both vanilla and hipri mobile types.
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_MOBILE, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
+        assertFalse(upstreamIterator.hasNext());
+    }
+
+    @Test
+    public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
+        mHasTelephonyManager = false;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
+        assertFalse(upstreamIterator.hasNext());
+    }
+
+    @Test
+    public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
+        mHasTelephonyManager = false;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
+        assertTrue(upstreamIterator.hasNext());
+        assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
+        assertFalse(upstreamIterator.hasNext());
+    }
 }
diff --git a/tools/locked_region_code_injection/Android.mk b/tools/locked_region_code_injection/Android.mk
new file mode 100644
index 0000000..0aed0ce
--- /dev/null
+++ b/tools/locked_region_code_injection/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_JAR_MANIFEST := manifest.txt
+LOCAL_MODULE := lockedregioncodeinjection
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    asm-5.2 \
+    asm-commons-5.2 \
+    asm-tree-5.2 \
+    asm-analysis-5.2
+
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/locked_region_code_injection/manifest.txt b/tools/locked_region_code_injection/manifest.txt
new file mode 100644
index 0000000..4b9de00
--- /dev/null
+++ b/tools/locked_region_code_injection/manifest.txt
@@ -0,0 +1 @@
+Main-Class: lockedregioncodeinjection.Main
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
new file mode 100644
index 0000000..9374f23
--- /dev/null
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
@@ -0,0 +1,241 @@
+/*
+ * 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 lockedregioncodeinjection;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.TryCatchBlockSorter;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
+import org.objectweb.asm.tree.analysis.Analyzer;
+import org.objectweb.asm.tree.analysis.AnalyzerException;
+import org.objectweb.asm.tree.analysis.BasicValue;
+import org.objectweb.asm.tree.analysis.Frame;
+
+/**
+ * This visitor does two things:
+ *
+ * 1. Finds all the MONITOR_ENTER / MONITOR_EXIT in the byte code and insert the corresponding pre
+ * and post methods calls should it matches one of the given target type in the Configuration.
+ *
+ * 2. Find all methods that are synchronized and insert pre method calls in the beginning and post
+ * method calls just before all return instructions.
+ */
+class LockFindingClassVisitor extends ClassVisitor {
+    private String className = null;
+    private final List<LockTarget> targets;
+
+    public LockFindingClassVisitor(List<LockTarget> targets, ClassVisitor chain) {
+        super(Utils.ASM_VERSION, chain);
+        this.targets = targets;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+            String[] exceptions) {
+        assert this.className != null;
+        MethodNode mn = new TryCatchBlockSorter(null, access, name, desc, signature, exceptions);
+        MethodVisitor chain = super.visitMethod(access, name, desc, signature, exceptions);
+        return new LockFindingMethodVisitor(this.className, mn, chain);
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature, String superName,
+            String[] interfaces) {
+        this.className = name;
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    class LockFindingMethodVisitor extends MethodVisitor {
+        private String owner;
+        private MethodVisitor chain;
+
+        public LockFindingMethodVisitor(String owner, MethodNode mn, MethodVisitor chain) {
+            super(Opcodes.ASM5, mn);
+            assert owner != null;
+            this.owner = owner;
+            this.chain = chain;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void visitEnd() {
+            MethodNode mn = (MethodNode) mv;
+
+            Analyzer a = new Analyzer(new LockTargetStateAnalysis(targets));
+
+            LockTarget ownerMonitor = null;
+            if ((mn.access & Opcodes.ACC_SYNCHRONIZED) != 0) {
+                for (LockTarget t : targets) {
+                    if (t.getTargetDesc().equals("L" + owner + ";")) {
+                        ownerMonitor = t;
+                    }
+                }
+            }
+
+            try {
+                a.analyze(owner, mn);
+            } catch (AnalyzerException e) {
+                e.printStackTrace();
+            }
+            InsnList instructions = mn.instructions;
+
+            Frame[] frames = a.getFrames();
+            List<Frame> frameMap = new LinkedList<>();
+            frameMap.addAll(Arrays.asList(frames));
+
+            List<List<TryCatchBlockNode>> handlersMap = new LinkedList<>();
+
+            for (int i = 0; i < instructions.size(); i++) {
+                handlersMap.add(a.getHandlers(i));
+            }
+
+            if (ownerMonitor != null) {
+                AbstractInsnNode s = instructions.getFirst();
+                MethodInsnNode call = new MethodInsnNode(Opcodes.INVOKESTATIC,
+                        ownerMonitor.getPreOwner(), ownerMonitor.getPreMethod(), "()V", false);
+                insertMethodCallBefore(mn, frameMap, handlersMap, s, 0, call);
+            }
+
+            for (int i = 0; i < instructions.size(); i++) {
+                AbstractInsnNode s = instructions.get(i);
+
+                if (s.getOpcode() == Opcodes.MONITORENTER) {
+                    Frame f = frameMap.get(i);
+                    BasicValue operand = (BasicValue) f.getStack(f.getStackSize() - 1);
+                    if (operand instanceof LockTargetState) {
+                        LockTargetState state = (LockTargetState) operand;
+                        for (int j = 0; j < state.getTargets().size(); j++) {
+                            LockTarget target = state.getTargets().get(j);
+                            MethodInsnNode call = new MethodInsnNode(Opcodes.INVOKESTATIC,
+                                    target.getPreOwner(), target.getPreMethod(), "()V", false);
+                            insertMethodCallAfter(mn, frameMap, handlersMap, s, i, call);
+                        }
+                    }
+                }
+
+                if (s.getOpcode() == Opcodes.MONITOREXIT) {
+                    Frame f = frameMap.get(i);
+                    BasicValue operand = (BasicValue) f.getStack(f.getStackSize() - 1);
+                    if (operand instanceof LockTargetState) {
+                        LockTargetState state = (LockTargetState) operand;
+                        for (int j = 0; j < state.getTargets().size(); j++) {
+                            LockTarget target = state.getTargets().get(j);
+                            MethodInsnNode call = new MethodInsnNode(Opcodes.INVOKESTATIC,
+                                    target.getPostOwner(), target.getPostMethod(), "()V", false);
+                            insertMethodCallAfter(mn, frameMap, handlersMap, s, i, call);
+                        }
+                    }
+                }
+
+                if (ownerMonitor != null && (s.getOpcode() == Opcodes.RETURN
+                        || s.getOpcode() == Opcodes.ARETURN || s.getOpcode() == Opcodes.DRETURN
+                        || s.getOpcode() == Opcodes.FRETURN || s.getOpcode() == Opcodes.IRETURN)) {
+                    MethodInsnNode call =
+                            new MethodInsnNode(Opcodes.INVOKESTATIC, ownerMonitor.getPostOwner(),
+                                    ownerMonitor.getPostMethod(), "()V", false);
+                    insertMethodCallBefore(mn, frameMap, handlersMap, s, i, call);
+                    i++; // Skip ahead. Otherwise, we will revisit this instruction again.
+                }
+            }
+            super.visitEnd();
+            mn.accept(chain);
+        }
+    }
+
+    public static void insertMethodCallBefore(MethodNode mn, List<Frame> frameMap,
+            List<List<TryCatchBlockNode>> handlersMap, AbstractInsnNode node, int index,
+            MethodInsnNode call) {
+        List<TryCatchBlockNode> handlers = handlersMap.get(index);
+        InsnList instructions = mn.instructions;
+        LabelNode end = new LabelNode();
+        instructions.insert(node, end);
+        frameMap.add(index, null);
+        handlersMap.add(index, null);
+        instructions.insertBefore(node, call);
+        frameMap.add(index, null);
+        handlersMap.add(index, null);
+
+        LabelNode start = new LabelNode();
+        instructions.insert(node, start);
+        frameMap.add(index, null);
+        handlersMap.add(index, null);
+        updateCatchHandler(mn, handlers, start, end, handlersMap);
+    }
+
+    public static void insertMethodCallAfter(MethodNode mn, List<Frame> frameMap,
+            List<List<TryCatchBlockNode>> handlersMap, AbstractInsnNode node, int index,
+            MethodInsnNode call) {
+        List<TryCatchBlockNode> handlers = handlersMap.get(index + 1);
+        InsnList instructions = mn.instructions;
+
+        LabelNode end = new LabelNode();
+        instructions.insert(node, end);
+        frameMap.add(index + 1, null);
+        handlersMap.add(index + 1, null);
+
+        instructions.insert(node, call);
+        frameMap.add(index + 1, null);
+        handlersMap.add(index + 1, null);
+
+        LabelNode start = new LabelNode();
+        instructions.insert(node, start);
+        frameMap.add(index + 1, null);
+        handlersMap.add(index + 1, null);
+
+        updateCatchHandler(mn, handlers, start, end, handlersMap);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static void updateCatchHandler(MethodNode mn, List<TryCatchBlockNode> handlers,
+            LabelNode start, LabelNode end, List<List<TryCatchBlockNode>> handlersMap) {
+        if (handlers == null || handlers.size() == 0) {
+            return;
+        }
+
+        InsnList instructions = mn.instructions;
+        List<TryCatchBlockNode> newNodes = new ArrayList<>(handlers.size());
+        for (TryCatchBlockNode handler : handlers) {
+            if (!(instructions.indexOf(handler.start) <= instructions.indexOf(start)
+                    && instructions.indexOf(end) <= instructions.indexOf(handler.end))) {
+                TryCatchBlockNode newNode =
+                        new TryCatchBlockNode(start, end, handler.handler, handler.type);
+                newNodes.add(newNode);
+                for (int i = instructions.indexOf(start); i <= instructions.indexOf(end); i++) {
+                    if (handlersMap.get(i) == null) {
+                        handlersMap.set(i, new ArrayList<>());
+                    }
+                    handlersMap.get(i).add(newNode);
+                }
+            } else {
+                for (int i = instructions.indexOf(start); i <= instructions.indexOf(end); i++) {
+                    if (handlersMap.get(i) == null) {
+                        handlersMap.set(i, new ArrayList<>());
+                    }
+                    handlersMap.get(i).add(handler);
+                }
+            }
+        }
+        mn.tryCatchBlocks.addAll(0, newNodes);
+    }
+}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTarget.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTarget.java
new file mode 100644
index 0000000..c5e59e3
--- /dev/null
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTarget.java
@@ -0,0 +1,61 @@
+/*
+ * 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 lockedregioncodeinjection;
+
+/**
+ * Represent a specific class that is used for synchronization. A pre and post method can be
+ * specified to by the user to be called right after monitor_enter and after monitor_exit
+ * respectively.
+ */
+public class LockTarget {
+    public static final LockTarget NO_TARGET = new LockTarget("", null, null);
+
+    private final String targetDesc;
+    private final String pre;
+    private final String post;
+
+    public LockTarget(String targetDesc, String pre, String post) {
+        this.targetDesc = targetDesc;
+        this.pre = pre;
+        this.post = post;
+    }
+
+    public String getTargetDesc() {
+        return targetDesc;
+    }
+
+    public String getPre() {
+        return pre;
+    }
+
+    public String getPreOwner() {
+        return pre.substring(0, pre.lastIndexOf('.'));
+    }
+
+    public String getPreMethod() {
+        return pre.substring(pre.lastIndexOf('.') + 1);
+    }
+
+    public String getPost() {
+        return post;
+    }
+
+    public String getPostOwner() {
+        return post.substring(0, post.lastIndexOf('.'));
+    }
+
+    public String getPostMethod() {
+        return post.substring(post.lastIndexOf('.') + 1);
+    }
+}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetState.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetState.java
new file mode 100644
index 0000000..99d8418
--- /dev/null
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetState.java
@@ -0,0 +1,34 @@
+/*
+ * 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 lockedregioncodeinjection;
+
+import java.util.List;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.analysis.BasicValue;
+
+public class LockTargetState extends BasicValue {
+    private final List<LockTarget> lockTargets;
+
+    /**
+     * @param type
+     */
+    public LockTargetState(Type type, List<LockTarget> lockTargets) {
+        super(type);
+        this.lockTargets = lockTargets;
+    }
+
+    public List<LockTarget> getTargets() {
+        return lockTargets;
+    }
+}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
new file mode 100644
index 0000000..1002c88
--- /dev/null
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
@@ -0,0 +1,109 @@
+/*
+ * 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 lockedregioncodeinjection;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.analysis.AnalyzerException;
+import org.objectweb.asm.tree.analysis.BasicInterpreter;
+import org.objectweb.asm.tree.analysis.BasicValue;
+
+/**
+ * A simple dataflow analysis to determine if the operands on the stack must be one of target lock
+ * class type.
+ */
+public class LockTargetStateAnalysis extends BasicInterpreter {
+
+    private final List<LockTarget> targetLocks;
+
+    public LockTargetStateAnalysis(List<LockTarget> targetLocks) {
+        this.targetLocks = targetLocks;
+    }
+
+    @Override
+    public BasicValue naryOperation(AbstractInsnNode inst, @SuppressWarnings("rawtypes") List args)
+            throws AnalyzerException {
+        // We target the return type of any invocation.
+
+        @SuppressWarnings("unchecked")
+        BasicValue base = super.naryOperation(inst, args);
+        if (!(inst instanceof MethodInsnNode)) {
+            return base;
+        }
+
+        MethodInsnNode invoke = (MethodInsnNode) inst;
+        Type returnType = Type.getReturnType(invoke.desc);
+        if (returnType.equals(Type.VOID_TYPE)) {
+            return base;
+        }
+
+        List<LockTarget> types = new ArrayList<>();
+
+        for (LockTarget target : targetLocks) {
+            if (returnType.getDescriptor().equals(target.getTargetDesc())) {
+                types.add(target);
+            }
+        }
+
+        return new LockTargetState(base.getType(), types);
+    }
+
+    @Override
+    public BasicValue newValue(Type type) {
+        BasicValue base = super.newValue(type);
+        List<LockTarget> types = new ArrayList<>();
+
+        if (type == null) {
+            return base;
+        }
+        for (LockTarget target : targetLocks) {
+            if (type.getDescriptor().equals(target.getTargetDesc())) {
+                types.add(target);
+            }
+        }
+
+        if (types.isEmpty()) {
+            return base;
+        }
+
+        return new LockTargetState(base.getType(), types);
+    }
+
+    @Override
+    public BasicValue merge(BasicValue v1, BasicValue v2) {
+        BasicValue base = super.merge(v1, v2);
+
+        if (!(v1 instanceof LockTargetState)) {
+            return base;
+        }
+        if (!(v2 instanceof LockTargetState)) {
+            return base;
+        }
+
+        LockTargetState state1 = (LockTargetState) v1;
+        LockTargetState state2 = (LockTargetState) v2;
+
+        List<LockTarget> newList = new ArrayList<>(state1.getTargets());
+        for (LockTarget otherTarget : state2.getTargets()) {
+            if (!newList.contains(otherTarget)) {
+                newList.add(otherTarget);
+            }
+        }
+
+        return new LockTargetState(base.getType(), newList);
+    }
+}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java
new file mode 100644
index 0000000..edb9a49
--- /dev/null
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java
@@ -0,0 +1,104 @@
+/*
+ * 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 lockedregioncodeinjection;
+
+import java.io.BufferedInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+public class Main {
+    public static void main(String[] args) throws IOException {
+        String inJar = null;
+        String outJar = null;
+
+        String legacyTargets = null;
+        String legacyPreMethods = null;
+        String legacyPostMethods = null;
+        for (int i = 0; i < args.length; i++) {
+            if ("-i".equals(args[i].trim())) {
+                i++;
+                inJar = args[i].trim();
+            } else if ("-o".equals(args[i].trim())) {
+                i++;
+                outJar = args[i].trim();
+            } else if ("--targets".equals(args[i].trim())) {
+                i++;
+                legacyTargets = args[i].trim();
+            } else if ("--pre".equals(args[i].trim())) {
+                i++;
+                legacyPreMethods = args[i].trim();
+            } else if ("--post".equals(args[i].trim())) {
+                i++;
+                legacyPostMethods = args[i].trim();
+            }
+
+        }
+
+        // TODO(acleung): Better help message than asserts.
+        assert inJar != null;
+        assert outJar != null;
+        assert legacyTargets == null || (legacyPreMethods != null && legacyPostMethods != null);
+
+        ZipFile zipSrc = new ZipFile(inJar);
+        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outJar));
+        List<LockTarget> targets = null;
+        if (legacyTargets != null) {
+            targets = Utils.getTargetsFromLegacyJackConfig(legacyTargets, legacyPreMethods,
+                    legacyPostMethods);
+        } else {
+            targets = Collections.emptyList();
+        }
+
+        Enumeration<? extends ZipEntry> srcEntries = zipSrc.entries();
+        while (srcEntries.hasMoreElements()) {
+            ZipEntry entry = srcEntries.nextElement();
+            ZipEntry newEntry = new ZipEntry(entry.getName());
+            zos.putNextEntry(newEntry);
+            BufferedInputStream bis = new BufferedInputStream(zipSrc.getInputStream(entry));
+
+            if (entry.getName().endsWith(".class")) {
+                convert(bis, zos, targets);
+            } else {
+                while (bis.available() > 0) {
+                    zos.write(bis.read());
+                }
+                zos.closeEntry();
+                bis.close();
+            }
+        }
+        zos.finish();
+        zos.close();
+        zipSrc.close();
+    }
+
+    private static void convert(InputStream in, OutputStream out, List<LockTarget> targets)
+            throws IOException {
+        ClassReader cr = new ClassReader(in);
+        ClassWriter cw = new ClassWriter(0);
+        LockFindingClassVisitor cv = new LockFindingClassVisitor(targets, cw);
+        cr.accept(cv, 0);
+        byte[] data = cw.toByteArray();
+        out.write(data);
+    }
+}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
new file mode 100644
index 0000000..d2a2e7b
--- /dev/null
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
@@ -0,0 +1,46 @@
+/*
+ * 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 lockedregioncodeinjection;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.objectweb.asm.Opcodes;
+
+public class Utils {
+
+    public static final int ASM_VERSION = Opcodes.ASM5;
+
+    /**
+     * Reads a comma separated configuration similar to the Jack definition.
+     */
+    public static List<LockTarget> getTargetsFromLegacyJackConfig(String classList,
+            String requestList, String resetList) {
+
+        String[] classes = classList.split(",");
+        String[] requests = requestList.split(",");
+        String[] resets = resetList.split(",");
+
+        int total = classes.length;
+        assert requests.length == total;
+        assert resets.length == total;
+
+        List<LockTarget> config = new ArrayList<LockTarget>();
+
+        for (int i = 0; i < total; i++) {
+            config.add(new LockTarget(classes[i], requests[i], resets[i]));
+        }
+
+        return config;
+    }
+}
diff --git a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
new file mode 100644
index 0000000..1d4f2d4
--- /dev/null
+++ b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
@@ -0,0 +1,231 @@
+/*
+ * 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 lockedregioncodeinjection;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * To run the unit tests:
+ *
+ * <pre>
+ * <code>
+ * set -x
+ *
+ * # Clean
+ * rm -fr out/*
+ *
+ * # Make booster
+ * javac -cp lib/asm-all-5.2.jar src&#47;*&#47;*.java -d out/
+ * pushd out
+ * jar cfe lockedregioncodeinjection.jar lockedregioncodeinjection.Main *&#47;*.class
+ * popd
+ *
+ * # Make unit tests.
+ * javac -cp lib/junit-4.12.jar test&#47;*&#47;*.java -d out/
+ *
+ * pushd out
+ * jar cfe test_input.jar lockedregioncodeinjection.Test *&#47;*.class
+ * popd
+ *
+ * # Run tool on unit tests.
+ * java -ea -cp lib/asm-all-5.2.jar:out/lockedregioncodeinjection.jar \
+ *     lockedregioncodeinjection.Main \
+ *     -i out/test_input.jar -o out/test_output.jar \
+ *     --targets 'Llockedregioncodeinjection/TestTarget;' \
+ *     --pre     'lockedregioncodeinjection/TestTarget.boost' \
+ *     --post    'lockedregioncodeinjection/TestTarget.unboost'
+ *
+ * # Run unit tests.
+ * java -ea -cp lib/hamcrest-core-1.3.jar:lib/junit-4.12.jar:out/test_output.jar \
+ *     org.junit.runner.JUnitCore lockedregioncodeinjection.TestMain
+ * </code>
+ * </pre>
+ */
+public class TestMain {
+    @Test
+    public void testSimpleSynchronizedBlock() {
+        TestTarget.resetCount();
+        TestTarget t = new TestTarget();
+
+        Assert.assertEquals(TestTarget.boostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+
+        synchronized (t) {
+            Assert.assertEquals(TestTarget.boostCount, 1);
+            Assert.assertEquals(TestTarget.unboostCount, 0);
+            TestTarget.invoke();
+        }
+
+        Assert.assertEquals(TestTarget.boostCount, 1);
+        Assert.assertEquals(TestTarget.unboostCount, 1);
+        Assert.assertEquals(TestTarget.invokeCount, 1);
+    }
+
+    @Test
+    public void testSimpleSynchronizedMethod() {
+        TestTarget.resetCount();
+        TestTarget t = new TestTarget();
+
+        Assert.assertEquals(TestTarget.boostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+
+        t.synchronizedCall();
+
+        Assert.assertEquals(TestTarget.boostCount, 1);
+        Assert.assertEquals(TestTarget.unboostCount, 1);
+        Assert.assertEquals(TestTarget.invokeCount, 1);
+    }
+
+    @Test
+    public void testSimpleSynchronizedMethod2() {
+        TestTarget.resetCount();
+        TestTarget t = new TestTarget();
+
+        Assert.assertEquals(TestTarget.boostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+
+        t.synchronizedCallReturnInt();
+
+        Assert.assertEquals(TestTarget.boostCount, 1);
+        Assert.assertEquals(TestTarget.unboostCount, 1);
+        Assert.assertEquals(TestTarget.invokeCount, 1);
+    }
+
+    @Test
+    public void testSimpleSynchronizedMethod3() {
+        TestTarget.resetCount();
+        TestTarget t = new TestTarget();
+
+        Assert.assertEquals(TestTarget.boostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+
+        t.synchronizedCallReturnObject();
+
+        Assert.assertEquals(TestTarget.boostCount, 1);
+        Assert.assertEquals(TestTarget.unboostCount, 1);
+        Assert.assertEquals(TestTarget.invokeCount, 1);
+    }
+
+    @SuppressWarnings("unused")
+    @Test
+    public void testCaughtException() {
+        TestTarget.resetCount();
+        TestTarget t = new TestTarget();
+        boolean caughtException = false;
+
+        Assert.assertEquals(TestTarget.boostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+
+        try {
+            synchronized (t) {
+                Assert.assertEquals(TestTarget.boostCount, 1);
+                Assert.assertEquals(TestTarget.unboostCount, 0);
+                if (true) {
+                    throw new RuntimeException();
+                }
+                TestTarget.invoke();
+            }
+        } catch (Throwable e) {
+            caughtException = true;
+        }
+
+        Assert.assertEquals(TestTarget.boostCount, 1);
+        Assert.assertEquals(TestTarget.unboostCount, 1);
+        Assert.assertEquals(TestTarget.invokeCount, 0); // Not called
+        Assert.assertTrue(caughtException);
+    }
+
+    @SuppressWarnings("unused")
+    private void testUncaughtException() {
+        TestTarget t = new TestTarget();
+        synchronized (t) {
+            if (true) {
+                throw new RuntimeException();
+            }
+            TestTarget.invoke();
+        }
+    }
+
+    @SuppressWarnings("unused")
+    @Test
+    public void testHandledFinally() {
+        TestTarget.resetCount();
+        try {
+            testUncaughtException();
+        } catch (Throwable t) {
+
+        }
+        Assert.assertEquals(TestTarget.boostCount, 1);
+        Assert.assertEquals(TestTarget.unboostCount, 1);
+        Assert.assertEquals(TestTarget.invokeCount, 0); // Not called
+    }
+
+    @Test
+    public void testNestedSynchronizedBlock() {
+        TestTarget.resetCount();
+        TestTarget t = new TestTarget();
+
+        Assert.assertEquals(TestTarget.boostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+
+        synchronized (t) {
+            synchronized (t) {
+                synchronized (t) {
+                    synchronized (t) {
+                        synchronized (t) {
+                            synchronized (t) {
+                                Assert.assertEquals(TestTarget.boostCount, 6);
+                                Assert.assertEquals(TestTarget.unboostCount, 0);
+                                TestTarget.invoke();
+                            }
+                            Assert.assertEquals(TestTarget.unboostCount, 1);
+                        }
+                        Assert.assertEquals(TestTarget.unboostCount, 2);
+                    }
+                    Assert.assertEquals(TestTarget.unboostCount, 3);
+                }
+                Assert.assertEquals(TestTarget.unboostCount, 4);
+            }
+            Assert.assertEquals(TestTarget.unboostCount, 5);
+        }
+
+        Assert.assertEquals(TestTarget.boostCount, 6);
+        Assert.assertEquals(TestTarget.unboostCount, 6);
+        Assert.assertEquals(TestTarget.invokeCount, 1);
+    }
+
+    @Test
+    public void testMethodWithControlFlow() {
+        TestTarget.resetCount();
+        TestTarget t = new TestTarget();
+
+        Assert.assertEquals(TestTarget.boostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+
+        if ((t.hashCode() + " ").contains("1")) {
+            t.synchronizedCall();
+        } else {
+            t.synchronizedCall();
+        }
+
+        // Should only be boosted once.
+        Assert.assertEquals(TestTarget.boostCount, 1);
+        Assert.assertEquals(TestTarget.unboostCount, 1);
+        Assert.assertEquals(TestTarget.invokeCount, 1);
+    }
+}
diff --git a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestTarget.java b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestTarget.java
new file mode 100644
index 0000000..8e7d478
--- /dev/null
+++ b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestTarget.java
@@ -0,0 +1,52 @@
+/*
+ * 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 lockedregioncodeinjection;
+
+public class TestTarget {
+  public static int boostCount = 0;
+  public static int unboostCount = 0;
+  public static int invokeCount = 0;
+
+  public static void boost() {
+    boostCount++;
+  }
+
+  public static void unboost() {
+    unboostCount++;
+  }
+
+  public static void invoke() {
+    invokeCount++;
+  }
+
+  public static void resetCount() {
+    boostCount = 0;
+    unboostCount = 0;
+    invokeCount = 0;
+  }
+
+  public synchronized void synchronizedCall() {
+    invoke();
+  }
+
+  public synchronized int synchronizedCallReturnInt() {
+    invoke();
+    return 0;
+  }
+
+  public synchronized Object synchronizedCallReturnObject() {
+    invoke();
+    return this;
+  }
+}