Merge "Snap for 5798008 from 3e85da2d89c0e7ef61b5bcc24a6b44bfa57f2c12 to sdk-release" into sdk-release
diff --git a/Android.bp b/Android.bp
index 9cc17ea..c194929 100644
--- a/Android.bp
+++ b/Android.bp
@@ -752,14 +752,26 @@
     name: "framework",
     defaults: ["framework-defaults"],
     javac_shard_size: 150,
+    required: [
+        "framework-platform-compat-config",
+        "libcore-platform-compat-config",
+    ],
 }
 
 java_library {
     name: "framework-annotation-proc",
     defaults: ["framework-defaults"],
     installable: false,
-    // Use UsedByApps annotation processor
-    plugins: ["unsupportedappusage-annotation-processor"],
+    plugins: [
+        "unsupportedappusage-annotation-processor",
+        "compat-changeid-annotation-processor",
+    ],
+}
+
+platform_compat_config {
+    name: "framework-platform-compat-config",
+    prefix: "framework",
+    src: ":framework-annotation-proc",
 }
 
 // A library including just UnsupportedAppUsage.java classes.
@@ -1272,11 +1284,6 @@
     srcs_lib: "framework",
     srcs_lib_whitelist_dirs: frameworks_base_subdirs,
     srcs_lib_whitelist_pkgs: packages_to_document,
-    libs: [
-        "ext",
-        "framework",
-        "voip-common",
-    ],
     local_sourcepaths: frameworks_base_subdirs,
     installable: false,
     annotations_enabled: true,
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 543f0ed..e731138 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,6 +9,8 @@
 
 hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
+ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
+
 owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
 
 shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/api/current.txt b/api/current.txt
index 015874d..188258b 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -11361,6 +11361,9 @@
     field public static final String FEATURE_SENSOR_RELATIVE_HUMIDITY = "android.hardware.sensor.relative_humidity";
     field public static final String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter";
     field public static final String FEATURE_SENSOR_STEP_DETECTOR = "android.hardware.sensor.stepdetector";
+    field public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+    field public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd";
+    field public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc";
     field public static final String FEATURE_SIP = "android.software.sip";
     field public static final String FEATURE_SIP_VOIP = "android.software.sip.voip";
     field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore";
@@ -37412,7 +37415,7 @@
     field public static final String CONTENT_ID = "cid";
     field public static final String CONTENT_LOCATION = "cl";
     field public static final String CONTENT_TYPE = "ct";
-    field public static final android.net.Uri CONTENT_URI;
+    field @NonNull public static final android.net.Uri CONTENT_URI;
     field public static final String CT_START = "ctt_s";
     field public static final String CT_TYPE = "ctt_t";
     field public static final String FILENAME = "fn";
@@ -42104,6 +42107,7 @@
 
   public class CarrierConfigManager {
     method @Nullable public android.os.PersistableBundle getConfig();
+    method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(String, int);
     method @Nullable public android.os.PersistableBundle getConfigForSubId(int);
     method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
     method public void notifyConfigChangedForSubId(int);
@@ -42281,6 +42285,10 @@
     field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
   }
 
+  public static final class CarrierConfigManager.Ims {
+    field public static final String KEY_PREFIX = "ims.";
+  }
+
   public abstract class CellIdentity implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public CharSequence getOperatorAlphaLong();
@@ -42463,6 +42471,7 @@
     method public int getBitErrorRate();
     method public int getDbm();
     method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
+    method public int getRssi();
     method public int getTimingAdvance();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
@@ -70466,7 +70475,7 @@
     method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String);
     method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @NonNull java.util.function.Supplier<java.lang.String>);
     method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Object);
-    method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, Object[]);
+    method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Object[]);
     method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Throwable);
     method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable Throwable, @NonNull java.util.function.Supplier<java.lang.String>);
     method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String);
diff --git a/api/removed.txt b/api/removed.txt
index f40b146..0795438 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -278,7 +278,7 @@
 package android.icu.util {
 
   public class JapaneseCalendar extends android.icu.util.GregorianCalendar {
-    field public static final int CURRENT_ERA;
+    field @Deprecated public static final int CURRENT_ERA;
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 593b45d..9200ce5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7424,6 +7424,7 @@
   public abstract class ImsFeature {
     ctor public ImsFeature();
     method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public final int getSlotIndex();
     method public abstract void onFeatureReady();
     method public abstract void onFeatureRemoved();
     method public final void setFeatureState(int);
@@ -7437,7 +7438,7 @@
     field public static final int STATE_UNAVAILABLE = 0; // 0x0
   }
 
-  @Deprecated public static class ImsFeature.Capabilities {
+  public static class ImsFeature.Capabilities {
     field @Deprecated protected int mCapabilities;
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 144e5f5..7ea9a26 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1521,6 +1521,11 @@
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int);
   }
 
+  public class PhoneNumberUtils {
+    method public static int getMinMatchForTest();
+    method public static void setMinMatchForTest(int);
+  }
+
   public class ServiceState implements android.os.Parcelable {
     method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo);
     method public void setCdmaSystemAndNetworkId(int, int);
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index e17f735..3e5877b 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -40,24 +40,6 @@
         "audioplay.cpp",
     ],
 
-    product_variables: {
-        product_is_iot: {
-            shared_libs: [
-                "libandroidthings",
-                "libchrome",
-            ],
-            srcs: [
-                "iot/iotbootanimation_main.cpp",
-                "iot/BootAction.cpp",
-                "iot/BootParameters.cpp",
-            ],
-            exclude_srcs: [
-                "bootanimation_main.cpp",
-                "audioplay.cpp",
-            ],
-        },
-    },
-
     init_rc: ["bootanim.rc"],
 }
 
@@ -77,10 +59,4 @@
         "libGLESv1_CM",
         "libgui",
     ],
-
-    product_variables: {
-        product_is_iot: {
-            init_rc: ["iot/bootanim_iot.rc"],
-        },
-    },
 }
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index ed6c25d..95bdc4a 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -63,6 +63,10 @@
 
 #include "BootAnimation.h"
 
+#define ANIM_PATH_MAX 255
+#define STR(x)   #x
+#define STRTO(x) STR(x)
+
 namespace android {
 
 static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
@@ -94,7 +98,7 @@
 static const int TEXT_CENTER_VALUE = INT_MAX;
 static const int TEXT_MISSING_VALUE = INT_MIN;
 static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
-static const int ANIM_ENTRY_NAME_MAX = 256;
+static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
 static constexpr size_t TEXT_POS_LEN_MAX = 16;
 
 // ---------------------------------------------------------------------------
@@ -658,7 +662,7 @@
             animation.width = width;
             animation.height = height;
             animation.fps = fps;
-        } else if (sscanf(l, " %c %d %d %s #%6s %16s %16s",
+        } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s",
                           &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
             //ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
             //    pathType, count, pause, path, color, clockPos1, clockPos2);
diff --git a/cmds/bootanimation/iot/BootAction.cpp b/cmds/bootanimation/iot/BootAction.cpp
deleted file mode 100644
index fa79744..0000000
--- a/cmds/bootanimation/iot/BootAction.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.
- */
-
-#include "BootAction.h"
-
-#define LOG_TAG "BootAction"
-
-#include <dlfcn.h>
-
-#include <pio/peripheral_manager_client.h>
-#include <utils/Log.h>
-
-namespace android {
-
-BootAction::~BootAction() {
-    if (mLibHandle != nullptr) {
-        dlclose(mLibHandle);
-    }
-}
-
-bool BootAction::init(const std::string& libraryPath,
-                      const std::vector<ABootActionParameter>& parameters) {
-    APeripheralManagerClient* client = nullptr;
-    ALOGD("Connecting to peripheralmanager");
-    // Wait for peripheral manager to come up.
-    while (client == nullptr) {
-        client = APeripheralManagerClient_new();
-        if (client == nullptr) {
-          ALOGV("peripheralmanager is not up, sleeping before we check again.");
-          usleep(250000);
-        }
-    }
-    ALOGD("Peripheralmanager is up.");
-    APeripheralManagerClient_delete(client);
-
-
-    ALOGI("Loading boot action %s", libraryPath.c_str());
-    mLibHandle = dlopen(libraryPath.c_str(), RTLD_NOW);
-    if (mLibHandle == nullptr) {
-        ALOGE("Unable to load library at %s :: %s",
-              libraryPath.c_str(), dlerror());
-        return false;
-    }
-
-    void* loaded = nullptr;
-    if (!loadSymbol("boot_action_init", &loaded) || loaded == nullptr) {
-        return false;
-    }
-    mLibInit = reinterpret_cast<libInit>(loaded);
-
-    loaded = nullptr;
-    if (!loadSymbol("boot_action_shutdown", &loaded) || loaded == nullptr) {
-        return false;
-    }
-    mLibShutdown = reinterpret_cast<libShutdown>(loaded);
-
-    // StartPart is considered optional, if it isn't exported by the library
-    // we will still call init and shutdown.
-    loaded = nullptr;
-    if (!loadSymbol("boot_action_start_part", &loaded) || loaded == nullptr) {
-        ALOGI("No boot_action_start_part found, action will not be told when "
-              "Animation parts change.");
-    } else {
-        mLibStartPart = reinterpret_cast<libStartPart>(loaded);
-    }
-
-    ALOGD("Entering boot_action_init");
-    bool result = mLibInit(parameters.data(), parameters.size());
-    ALOGD("Returned from boot_action_init");
-    return result;
-}
-
-void BootAction::startPart(int partNumber, int playNumber) {
-    if (mLibStartPart == nullptr) return;
-
-    ALOGD("Entering boot_action_start_part");
-    mLibStartPart(partNumber, playNumber);
-    ALOGD("Returned from boot_action_start_part");
-}
-
-void BootAction::shutdown() {
-    ALOGD("Entering boot_action_shutdown");
-    mLibShutdown();
-    ALOGD("Returned from boot_action_shutdown");
-}
-
-bool BootAction::loadSymbol(const char* symbol, void** loaded) {
-    *loaded = dlsym(mLibHandle, symbol);
-    if (loaded == nullptr) {
-        ALOGE("Unable to load symbol : %s :: %s", symbol, dlerror());
-        return false;
-    }
-    return true;
-}
-
-}  // namespace android
diff --git a/cmds/bootanimation/iot/BootAction.h b/cmds/bootanimation/iot/BootAction.h
deleted file mode 100644
index 5e2495f..0000000
--- a/cmds/bootanimation/iot/BootAction.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _BOOTANIMATION_BOOTACTION_H
-#define _BOOTANIMATION_BOOTACTION_H
-
-#include <string>
-#include <vector>
-
-#include <boot_action/boot_action.h>  // libandroidthings native API.
-#include <utils/RefBase.h>
-
-namespace android {
-
-class BootAction : public RefBase {
-public:
-    ~BootAction();
-
-    // libraryPath is a fully qualified path to the target .so library.
-    bool init(const std::string& libraryPath,
-              const std::vector<ABootActionParameter>& parameters);
-
-    // The animation is going to start playing partNumber for the playCount'th
-    // time, update the action as needed.
-    // This is run in the same thread as the boot animation,
-    // you must not block here.
-    void startPart(int partNumber, int playCount);
-
-    // Shutdown the boot action, this will be called shortly before the
-    // process is shut down to allow time for cleanup.
-    void shutdown();
-
-private:
-    typedef bool (*libInit)(const ABootActionParameter* parameters,
-                            size_t num_parameters);
-    typedef void (*libStartPart)(int partNumber, int playNumber);
-    typedef void (*libShutdown)();
-
-    bool loadSymbol(const char* symbol, void** loaded);
-
-    void* mLibHandle = nullptr;
-    libInit mLibInit = nullptr;
-    libStartPart mLibStartPart = nullptr;
-    libShutdown mLibShutdown = nullptr;
-};
-
-}  // namespace android
-
-
-#endif  // _BOOTANIMATION_BOOTACTION_H
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
deleted file mode 100644
index da6ad0d..0000000
--- a/cmds/bootanimation/iot/BootParameters.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.
- */
-
-#include "BootParameters.h"
-
-#define LOG_TAG "BootParameters"
-
-#include <fcntl.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <base/json/json_parser.h>
-#include <base/json/json_reader.h>
-#include <base/json/json_value_converter.h>
-#include <utils/Log.h>
-
-using android::base::RemoveFileIfExists;
-using android::base::ReadFileToString;
-using base::JSONReader;
-using base::JSONValueConverter;
-using base::Value;
-
-namespace android {
-
-namespace {
-
-// Brightness and volume are stored as integer strings in next_boot.json.
-// They are divided by this constant to produce the actual float values in
-// range [0.0, 1.0]. This constant must match its counterpart in
-// DeviceManager.
-constexpr const float kFloatScaleFactor = 1000.0f;
-
-constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
-constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
-
-void swapBootConfigs() {
-    // rename() will fail if next_boot.json doesn't exist, so delete
-    // last_boot.json manually first.
-    std::string err;
-    if (!RemoveFileIfExists(kLastBootFile, &err))
-        ALOGE("Unable to delete last boot file: %s", err.c_str());
-
-    if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
-        ALOGE("Unable to swap boot files: %s", strerror(errno));
-
-    int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
-    if (fd == -1) {
-        ALOGE("Unable to create next boot file: %s", strerror(errno));
-    } else {
-        // Make next_boot.json writable to everyone so DeviceManagementService
-        // can save saved_parameters there.
-        if (fchmod(fd, DEFFILEMODE))
-            ALOGE("Unable to set next boot file permissions: %s", strerror(errno));
-        close(fd);
-    }
-}
-
-}  // namespace
-
-BootParameters::SavedBootParameters::SavedBootParameters()
-    : brightness(-kFloatScaleFactor), volume(-kFloatScaleFactor) {}
-
-void BootParameters::SavedBootParameters::RegisterJSONConverter(
-        JSONValueConverter<SavedBootParameters>* converter) {
-    converter->RegisterIntField("brightness", &SavedBootParameters::brightness);
-    converter->RegisterIntField("volume", &SavedBootParameters::volume);
-    converter->RegisterRepeatedString("param_names",
-                                      &SavedBootParameters::param_names);
-    converter->RegisterRepeatedString("param_values",
-                                      &SavedBootParameters::param_values);
-}
-
-BootParameters::BootParameters() {
-    swapBootConfigs();
-    loadParameters();
-}
-
-void BootParameters::loadParameters() {
-    std::string contents;
-    if (!ReadFileToString(kLastBootFile, &contents)) {
-        if (errno != ENOENT)
-            ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
-
-        return;
-    }
-
-    std::unique_ptr<Value> json = JSONReader::Read(contents);
-    if (json.get() == nullptr) {
-        return;
-    }
-
-    JSONValueConverter<SavedBootParameters> converter;
-    if (converter.Convert(*(json.get()), &mRawParameters)) {
-        mBrightness = mRawParameters.brightness / kFloatScaleFactor;
-        mVolume = mRawParameters.volume / kFloatScaleFactor;
-
-        if (mRawParameters.param_names.size() == mRawParameters.param_values.size()) {
-            for (size_t i = 0; i < mRawParameters.param_names.size(); i++) {
-                mParameters.push_back({
-                        .key = mRawParameters.param_names[i]->c_str(),
-                        .value = mRawParameters.param_values[i]->c_str()
-                });
-            }
-        } else {
-            ALOGW("Parameter names and values size mismatch");
-        }
-    }
-}
-
-}  // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
deleted file mode 100644
index c10bd44..0000000
--- a/cmds/bootanimation/iot/BootParameters.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _BOOTANIMATION_BOOT_PARAMETERS_H_
-#define _BOOTANIMATION_BOOT_PARAMETERS_H_
-
-#include <list>
-#include <vector>
-
-#include <base/json/json_value_converter.h>
-#include <boot_action/boot_action.h>  // libandroidthings native API.
-
-namespace android {
-
-// Provides access to the parameters set by DeviceManager.reboot().
-class BootParameters {
-public:
-    // Constructor loads the parameters for this boot and swaps the param files
-    // to clear the parameters for next boot.
-    BootParameters();
-
-    // Returns true if volume/brightness were explicitly set on reboot.
-    bool hasVolume() const { return mVolume >= 0; }
-    bool hasBrightness() const { return mBrightness >= 0; }
-
-    // Returns volume/brightness in [0,1], or -1 if unset.
-    float getVolume() const { return mVolume; }
-    float getBrightness() const { return mBrightness; }
-
-    // Returns the additional boot parameters that were set on reboot.
-    const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
-
-private:
-    // Raw boot saved_parameters loaded from .json.
-    struct SavedBootParameters {
-        int brightness;
-        int volume;
-        std::vector<std::unique_ptr<std::string>> param_names;
-        std::vector<std::unique_ptr<std::string>> param_values;
-
-        SavedBootParameters();
-        static void RegisterJSONConverter(
-                ::base::JSONValueConverter<SavedBootParameters>* converter);
-    };
-
-    void loadParameters();
-
-    float mVolume = -1.f;
-    float mBrightness = -1.f;
-    std::vector<ABootActionParameter> mParameters;
-
-    // ABootActionParameter is just a raw pointer so we need to keep the
-    // original strings around to avoid losing them.
-    SavedBootParameters mRawParameters;
-};
-
-}  // namespace android
-
-
-#endif  // _BOOTANIMATION_BOOT_PARAMETERS_H_
diff --git a/cmds/bootanimation/iot/bootanim_iot.rc b/cmds/bootanimation/iot/bootanim_iot.rc
deleted file mode 100644
index 2fc1336..0000000
--- a/cmds/bootanimation/iot/bootanim_iot.rc
+++ /dev/null
@@ -1,2 +0,0 @@
-on post-fs-data
-    mkdir /data/misc/bootanimation 0777 root root
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
deleted file mode 100644
index 00cef43..0000000
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "IotBootAnimation"
-
-#include <base/files/file_util.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <cutils/properties.h>
-#include <sys/resource.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <BootAnimation.h>
-
-#include "BootAction.h"
-#include "BootAnimationUtil.h"
-#include "BootParameters.h"
-
-using namespace android;
-
-// Create a typedef for readability.
-typedef android::BootAnimation::Animation Animation;
-
-namespace {
-
-constexpr const char* kDefaultLibName = "libbootaction.so";
-
-class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {
-public:
-    BootActionAnimationCallbacks(std::unique_ptr<BootParameters> bootParameters)
-        : mBootParameters(std::move(bootParameters)) {}
-
-    void init(const Vector<Animation::Part>&) override {
-        std::string library_path("/oem/lib/");
-
-        // This value is optionally provided by the user and will be written to
-        // /oem/oem.prop.
-        char property[PROP_VALUE_MAX] = {0};
-        property_get("ro.oem.bootactions.lib", property, kDefaultLibName);
-        library_path += property;
-
-        if (!::base::PathExists(::base::FilePath(library_path))) {
-            ALOGI("Skipping boot actions: %s does not exist", library_path.c_str());
-            return;
-        }
-
-        mBootAction = new BootAction();
-        if (!mBootAction->init(library_path, mBootParameters->getParameters())) {
-            mBootAction = NULL;
-        }
-    };
-
-    void playPart(int partNumber, const Animation::Part&, int playNumber) override {
-        if (mBootAction != nullptr) {
-            mBootAction->startPart(partNumber, playNumber);
-        }
-    };
-
-    void shutdown() override {
-        if (mBootAction != nullptr) {
-            // If we have a bootaction we want to wait until we are actually
-            // told to shut down. If the animation exits early keep the action
-            // running.
-            char value[PROPERTY_VALUE_MAX] = {0};
-            for (int exitRequested = 0; exitRequested == 0; ) {
-                property_get("service.bootanim.exit", value, "0");
-                exitRequested = atoi(value);
-
-                // Poll value at 10hz.
-                if (exitRequested == 0) {
-                  usleep(100000);
-                }
-            }
-
-            mBootAction->shutdown();
-            // Give it two seconds to shut down.
-            sleep(2);
-            mBootAction = nullptr;
-        }
-    };
-
-private:
-    std::unique_ptr<BootParameters> mBootParameters;
-    sp<BootAction> mBootAction = nullptr;
-};
-
-}  // namespace
-
-int main() {
-    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
-
-    // Clear our params for next boot no matter what.
-    std::unique_ptr<BootParameters> bootParameters(new BootParameters());
-
-    if (bootAnimationDisabled()) {
-        ALOGI("boot animation disabled");
-        return 0;
-    }
-
-    waitForSurfaceFlinger();
-
-    sp<ProcessState> proc(ProcessState::self());
-    ProcessState::self()->startThreadPool();
-
-    sp<BootAnimation> boot = new BootAnimation(
-            new BootActionAnimationCallbacks(std::move(bootParameters)));
-
-    IPCThreadState::self()->joinThreadPool();
-    return 0;
-}
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index d69dd79..847dda3 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -9,7 +9,6 @@
 #include <androidfw/ResourceTypes.h>
 #include <androidfw/StreamingZipInflater.h>
 #include <androidfw/ZipFileRO.h>
-#include <cutils/jstring.h>
 #include <cutils/properties.h>
 #include <private/android_filesystem_config.h> // for AID_SYSTEM
 #include <utils/SortedVector.h>
@@ -84,15 +83,9 @@
     }
 
     bool check_property(String16 property, String16 value) {
-        const char *prop;
-        const char *val;
-
-        prop = strndup16to8(property.string(), property.size());
         char propBuf[PROPERTY_VALUE_MAX];
-        property_get(prop, propBuf, NULL);
-        val = strndup16to8(value.string(), value.size());
-
-        return (strcmp(propBuf, val) == 0);
+        property_get(String8(property).c_str(), propBuf, NULL);
+        return String8(value) == propBuf;
     }
 
     int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name,
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1254f1a..f9b6219 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2871,6 +2871,9 @@
     // Additional pass-through fields opaque to statsd.
     // The DNS resolver Mainline module can add new fields here without requiring an OS update.
     optional android.stats.dnsresolver.DnsQueryEvents dns_query_events = 8 [(log_mode) = MODE_BYTES];
+
+    // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom.
+    optional int32 sampling_rate_denom = 9;
 }
 
 /**
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index b29e979..8233eee 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -35,8 +35,14 @@
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 StatsPuller::StatsPuller(const int tagId)
     : mTagId(tagId) {
-    mCoolDownNs = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
-    VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
+    auto pullAtomInfo = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId);
+    if (pullAtomInfo != StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+        mCoolDownNs = pullAtomInfo->second.coolDownNs;
+        VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
+    } else {
+        mCoolDownNs = 0;
+        VLOG("Creating puller for a non-recognised tag %d.", mTagId);
+    }
 }
 
 bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
diff --git a/config/boot-profile.txt b/config/boot-profile.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/boot-profile.txt
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d9c82ea..6c67191 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -703,6 +703,8 @@
 
         boolean autofillCompatibilityEnabled;
 
+        long[] disabledCompatChanges;
+
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
@@ -920,7 +922,8 @@
                 boolean enableBinderTracking, boolean trackAllocation,
                 boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                 CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
-                String buildSerial, boolean autofillCompatibilityEnabled) {
+                String buildSerial, boolean autofillCompatibilityEnabled,
+                long[] disabledCompatChanges) {
 
             if (services != null) {
                 if (false) {
@@ -968,6 +971,7 @@
             data.initProfilerInfo = profilerInfo;
             data.buildSerial = buildSerial;
             data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
+            data.disabledCompatChanges = disabledCompatChanges;
             sendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -5670,6 +5674,7 @@
         // Note when this process has started.
         Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
 
+        AppCompatCallbacks.install(data.disabledCompatChanges);
         mBoundApplication = data;
         mConfiguration = new Configuration(data.config);
         mCompatConfiguration = new Configuration(data.config);
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
new file mode 100644
index 0000000..17697db
--- /dev/null
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 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.app;
+
+import android.compat.Compatibility;
+import android.os.Process;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * App process implementation of the {@link Compatibility} API.
+ *
+ * @hide
+ */
+public final class AppCompatCallbacks extends Compatibility.Callbacks {
+
+    private static final String TAG = "Compatibility";
+
+    private final long[] mDisabledChanges;
+
+    /**
+     * Install this class into the current process.
+     *
+     * @param disabledChanges Set of compatibility changes that are disabled for this process.
+     */
+    public static void install(long[] disabledChanges) {
+        Compatibility.setCallbacks(new AppCompatCallbacks(disabledChanges));
+    }
+
+    private AppCompatCallbacks(long[] disabledChanges) {
+        mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length);
+        Arrays.sort(mDisabledChanges);
+    }
+
+    protected void reportChange(long changeId) {
+        Log.d(TAG, "Compat change reported: " + changeId + "; UID " + Process.myUid());
+        // TODO log via StatsLog
+    }
+
+    protected boolean isChangeEnabled(long changeId) {
+        if (Arrays.binarySearch(mDisabledChanges, changeId) < 0) {
+            // Not present in the disabled array
+            reportChange(changeId);
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index d478cd6..1f45fc5 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -67,7 +67,8 @@
             int debugMode, boolean enableBinderTracking, boolean trackAllocation,
             boolean restrictedBackupMode, boolean persistent, in Configuration config,
             in CompatibilityInfo compatInfo, in Map services,
-            in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled);
+            in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled,
+            in long[] disabledCompatChanges);
     void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
     void scheduleExit();
     void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 556ffa24..1c1fad3 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -1592,5 +1592,15 @@
             }
             return new JobInfo(this);
         }
+
+        /**
+         * @hide
+         */
+        public String summarize() {
+            final String service = (mJobService != null)
+                    ? mJobService.flattenToShortString()
+                    : "null";
+            return "JobInfo.Builder{job:" + mJobId + "/" + service + "}";
+        }
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index 79c0a3a..c79df17 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -428,6 +428,43 @@
     }
 
     /**
+     * Checks whether a value set presented by a bitmask has zero or single bit
+     *
+     * @param valueSet the value set presented by a bitmask
+     * @return true if the valueSet contains zero or single bit, otherwise false.
+     */
+    private static boolean hasSingleBit(int valueSet) {
+        return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0);
+    }
+
+    /**
+     * Checks whether the object contains none or single sample rate.
+     *
+     * @return true if the object contains none or single sample rate, otherwise false.
+     */
+    public boolean hasSingleSampleRate() {
+        return hasSingleBit(mSampleRate);
+    }
+
+    /**
+     * Checks whether the object contains none or single bits per sample.
+     *
+     * @return true if the object contains none or single bits per sample, otherwise false.
+     */
+    public boolean hasSingleBitsPerSample() {
+        return hasSingleBit(mBitsPerSample);
+    }
+
+    /**
+     * Checks whether the object contains none or single channel mode.
+     *
+     * @return true if the object contains none or single channel mode, otherwise false.
+     */
+    public boolean hasSingleChannelMode() {
+        return hasSingleBit(mChannelMode);
+    }
+
+    /**
      * Checks whether the audio feeding parameters are same.
      *
      * @param other the codec config to compare against
@@ -438,4 +475,58 @@
                 && other.mBitsPerSample == mBitsPerSample
                 && other.mChannelMode == mChannelMode);
     }
+
+    /**
+     * Checks whether another codec config has the similar feeding parameters.
+     * Any parameters with NONE value will be considered to be a wildcard matching.
+     *
+     * @param other the codec config to compare against
+     * @return true if the audio feeding parameters are similar, otherwise false.
+     */
+    public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) {
+        if (other == null || mCodecType != other.mCodecType) {
+            return false;
+        }
+        int sampleRate = other.mSampleRate;
+        if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE
+                || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+            sampleRate = mSampleRate;
+        }
+        int bitsPerSample = other.mBitsPerSample;
+        if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE
+                || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
+            bitsPerSample = mBitsPerSample;
+        }
+        int channelMode = other.mChannelMode;
+        if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE
+                || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) {
+            channelMode = mChannelMode;
+        }
+        return sameAudioFeedingParameters(new BluetoothCodecConfig(
+                mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode,
+                /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0,
+                /* specific4 */ 0));
+    }
+
+    /**
+     * Checks whether the codec specific parameters are the same.
+     *
+     * @param other the codec config to compare against
+     * @return true if the codec specific parameters are the same, otherwise false.
+     */
+    public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) {
+        if (other == null && mCodecType != other.mCodecType) {
+            return false;
+        }
+        // Currently we only care about the LDAC Playback Quality at CodecSpecific1
+        switch (mCodecType) {
+            case SOURCE_CODEC_TYPE_LDAC:
+                if (mCodecSpecific1 != other.mCodecSpecific1) {
+                    return false;
+                }
+                // fall through
+            default:
+                return true;
+        }
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 32bb681..8237d6a 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -88,6 +88,43 @@
         return Arrays.asList(c1).containsAll(Arrays.asList(c2));
     }
 
+    /**
+     * Checks whether the codec config matches the selectable capabilities.
+     * Any parameters of the codec config with NONE value will be considered a wildcard matching.
+     *
+     * @param codecConfig the codec config to compare against
+     * @return true if the codec config matches, otherwise false
+     */
+    public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) {
+        if (codecConfig == null || !codecConfig.hasSingleSampleRate()
+                || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) {
+            return false;
+        }
+        for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) {
+            if (codecConfig.getCodecType() != selectableConfig.getCodecType()) {
+                continue;
+            }
+            int sampleRate = codecConfig.getSampleRate();
+            if ((sampleRate & selectableConfig.getSampleRate()) == 0
+                    && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+                continue;
+            }
+            int bitsPerSample = codecConfig.getBitsPerSample();
+            if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0
+                    && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
+                continue;
+            }
+            int channelMode = codecConfig.getChannelMode();
+            if ((channelMode & selectableConfig.getChannelMode()) == 0
+                    && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) {
+                continue;
+            }
+            return true;
+        }
+        return false;
+    }
+
+
     @Override
     public int hashCode() {
         return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 05833b5..5d00f09 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -126,6 +126,17 @@
             "android.bluetooth.headsetclient.profile.action.RESULT";
 
     /**
+     * Intent that notifies about vendor specific event arrival. Events not defined in
+     * HFP spec will be matched with supported vendor event list and this intent will
+     * be broadcasted upon a match. Supported vendor events are of format of
+     * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx".
+     * Vendor event can be a response to an vendor specific command or unsolicited.
+     *
+     */
+    public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT =
+            "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT";
+
+    /**
      * Intent that notifies about the number attached to the last voice tag
      * recorded on AG.
      *
@@ -243,6 +254,28 @@
     public static final String EXTRA_CME_CODE =
             "android.bluetooth.headsetclient.extra.CME_CODE";
 
+    /**
+     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+     * indicates vendor ID.
+     */
+    public static final String EXTRA_VENDOR_ID =
+            "android.bluetooth.headsetclient.extra.VENDOR_ID";
+
+     /**
+     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+     * indicates vendor event code.
+     */
+    public static final String EXTRA_VENDOR_EVENT_CODE =
+            "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE";
+
+     /**
+     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+     * contains full vendor event including event code and full arguments.
+     */
+    public static final String EXTRA_VENDOR_EVENT_FULL_ARGS =
+            "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS";
+
+
     /* Extras for AG_FEATURES, extras type is boolean */
     // TODO verify if all of those are actually useful
     /**
@@ -588,6 +621,31 @@
     }
 
     /**
+     * Send vendor specific AT command.
+     *
+     * @param device remote device
+     * @param vendorId vendor number by Bluetooth SIG
+     * @param atCommand command to be sent. It start with + prefix and only one command at one time.
+     * @return <code>true</code> if command has been issued successfully; <code>false</code>
+     * otherwise.
+     */
+    public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId,
+                                             String atCommand) {
+        if (DBG) log("sendVendorSpecificCommand()");
+        final IBluetoothHeadsetClient service =
+                getService();
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return service.sendVendorAtCommand(device, vendorId, atCommand);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
      * Stops voice recognition.
      *
      * @param device remote device
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 99ff53d..4ab8c5f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3037,6 +3037,7 @@
             TELEPHONY_SERVICE,
             TELEPHONY_SUBSCRIPTION_SERVICE,
             CARRIER_CONFIG_SERVICE,
+            EUICC_SERVICE,
             TELECOM_SERVICE,
             CLIPBOARD_SERVICE,
             INPUT_METHOD_SERVICE,
@@ -3216,6 +3217,8 @@
      * @see android.telephony.SubscriptionManager
      * @see #CARRIER_CONFIG_SERVICE
      * @see android.telephony.CarrierConfigManager
+     * @see #EUICC_SERVICE
+     * @see android.telephony.euicc.EuiccManager
      * @see #INPUT_METHOD_SERVICE
      * @see android.view.inputmethod.InputMethodManager
      * @see #UI_MODE_SERVICE
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4f7f07b..f7c9635 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1949,6 +1949,30 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable UICC-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable eSE-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable SD-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports the OpenGL ES
      * <a href="http://www.khronos.org/registry/gles/extensions/ANDROID/ANDROID_extension_pack_es31a.txt">
      * Android Extension Pack</a>.
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 7d101b8..888380b 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -32,8 +32,8 @@
 import android.view.WindowManager.LayoutParams;
 
 /**
- * CompatibilityInfo class keeps the information about compatibility mode that the application is
- * running under.
+ * CompatibilityInfo class keeps the information about the screen compatibility mode that the
+ * application is running under.
  * 
  *  {@hide} 
  */
diff --git a/core/java/android/net/LinkQualityInfo.java b/core/java/android/net/LinkQualityInfo.java
index b6f8825..78d7787 100644
--- a/core/java/android/net/LinkQualityInfo.java
+++ b/core/java/android/net/LinkQualityInfo.java
@@ -24,8 +24,8 @@
  *  Class that represents useful attributes of generic network links
  *  such as the upload/download throughput or packet error rate.
  *  Generally speaking, you should be dealing with instances of
- *  LinkQualityInfo subclasses, such as {@link android.net.#WifiLinkQualityInfo}
- *  or {@link android.net.#MobileLinkQualityInfo} which provide additional
+ *  LinkQualityInfo subclasses, such as {@link android.net.WifiLinkQualityInfo}
+ *  or {@link android.net.MobileLinkQualityInfo} which provide additional
  *  information.
  *  @hide
  */
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 32735aa..4583483 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -233,22 +233,5 @@
                 Binder.restoreCallingIdentity(identity);
             }
         }
-
-        // Old methods; should go away
-        @Override
-        public void onProgressUpdated(int progress) throws RemoteException {
-            // TODO(b/111441001): remove from interface
-        }
-
-        @Override
-        public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
-            // TODO(b/111441001): remove from interface
-        }
-
-        @Override
-        public void onSectionComplete(String title, int status, int size, int durationMs)
-                throws RemoteException {
-            // TODO(b/111441001): remove from interface
-        }
     }
 }
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index b13e68d..0c3f291 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -123,6 +123,7 @@
             IBinder binder = callback.asBinder();
             try {
                 Callback cb = new Callback(callback, cookie);
+                unregister(callback);
                 binder.linkToDeath(cb, 0);
                 mCallbacks.put(binder, cb);
                 return true;
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 6025c34..2ba3982 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -25,6 +25,8 @@
 
 import dalvik.system.VMRuntime;
 
+import libcore.io.IoUtils;
+
 import java.io.Closeable;
 import java.io.FileDescriptor;
 import java.nio.ByteBuffer;
@@ -62,7 +64,7 @@
 
         mMemoryRegistration = new MemoryRegistration(mSize);
         mCleaner = Cleaner.create(mFileDescriptor,
-                new Closer(mFileDescriptor, mMemoryRegistration));
+                new Closer(mFileDescriptor.getInt$(), mMemoryRegistration));
     }
 
     /**
@@ -259,6 +261,9 @@
             mCleaner.clean();
             mCleaner = null;
         }
+
+        // Cleaner.clean doesn't clear the value of the file descriptor.
+        mFileDescriptor.setInt$(-1);
     }
 
     @Override
@@ -290,19 +295,24 @@
      * Cleaner that closes the FD
      */
     private static final class Closer implements Runnable {
+        // This is a copy of the FileDescriptor we're attached to, in order to avoid a reference
+        // cycle.
         private FileDescriptor mFd;
         private MemoryRegistration mMemoryReference;
 
-        private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
-            mFd = fd;
+        private Closer(int fd, MemoryRegistration memoryReference) {
+            mFd = new FileDescriptor();
+            mFd.setInt$(fd);
+            IoUtils.setFdOwner(mFd, this);
+
             mMemoryReference = memoryReference;
         }
 
         @Override
         public void run() {
-            try {
-                Os.close(mFd);
-            } catch (ErrnoException e) { /* swallow error */ }
+            IoUtils.closeQuietly(mFd);
+            mFd = null;
+
             mMemoryReference.release();
             mMemoryReference = null;
         }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index e8f4641..d438103 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -648,6 +648,31 @@
     }
 
     /**
+     * Notify the Zygote processes that boot completed.
+     */
+    public void bootCompleted() {
+        // Notify both the 32-bit and 64-bit zygote.
+        if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+            bootCompleted(Build.SUPPORTED_32_BIT_ABIS[0]);
+        }
+        if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+            bootCompleted(Build.SUPPORTED_64_BIT_ABIS[0]);
+        }
+    }
+
+    private void bootCompleted(String abi) {
+        try {
+            synchronized (mLock) {
+                ZygoteState state = openZygoteSocketIfNeeded(abi);
+                state.mZygoteOutputWriter.write("1\n--boot-completed\n");
+                state.mZygoteOutputWriter.flush();
+            }
+        } catch (Exception ex) {
+            throw new RuntimeException("Failed to inform zygote of boot_completed", ex);
+        }
+    }
+
+    /**
      * Push hidden API blacklisting exemptions into the zygote process(es).
      *
      * <p>The list of exemptions will take affect for all new processes forked from the zygote after
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b6ed22c..6f7d456 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -905,10 +905,12 @@
                 if (!mFlingScroller.isFinished()) {
                     mFlingScroller.forceFinished(true);
                     mAdjustScroller.forceFinished(true);
+                    onScrollerFinished(mFlingScroller);
                     onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                 } else if (!mAdjustScroller.isFinished()) {
                     mFlingScroller.forceFinished(true);
                     mAdjustScroller.forceFinished(true);
+                    onScrollerFinished(mAdjustScroller);
                 } else if (mLastDownEventY < mTopSelectionDividerTop) {
                     postChangeCurrentByOneFromLongPress(
                             false, ViewConfiguration.getLongPressTimeout());
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index fde01dd..750d698 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -195,7 +195,7 @@
         return applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp();
     }
 
-    private static boolean isDisclosureEnabled(Context context) {
+    public static boolean isDisclosureEnabled(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
                 Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 0) != 0;
     }
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index df8c6d0..b3b0ba1 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -27,6 +27,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.File;
 import java.io.FileInputStream;
 import java.util.Iterator;
 
@@ -38,6 +39,7 @@
     private static int sKernelWakelockUpdateVersion = 0;
     private static final String sWakelockFile = "/proc/wakelocks";
     private static final String sWakeupSourceFile = "/d/wakeup_sources";
+    private static final String sSysClassWakeupDir = "/sys/class/wakeup";
 
     private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
         Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
@@ -71,99 +73,125 @@
      * @return the updated data.
      */
     public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
-        byte[] buffer = new byte[32*1024];
-        int len = 0;
-        boolean wakeup_sources;
-        final long startTime = SystemClock.uptimeMillis();
+        boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists();
 
-        final int oldMask = StrictMode.allowThreadDiskReadsMask();
-        try {
-            FileInputStream is;
+        if (useSystemSuspend) {
+            // Get both kernel and native wakelock stats from SystemSuspend
+            updateVersion(staleStats);
+            if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+                Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
+                return null;
+            }
+            return removeOldStats(staleStats);
+        } else {
+            byte[] buffer = new byte[32*1024];
+            int len = 0;
+            boolean wakeup_sources;
+            final long startTime = SystemClock.uptimeMillis();
+
+            final int oldMask = StrictMode.allowThreadDiskReadsMask();
             try {
-                is = new FileInputStream(sWakelockFile);
-                wakeup_sources = false;
-            } catch (java.io.FileNotFoundException e) {
+                FileInputStream is;
                 try {
-                    is = new FileInputStream(sWakeupSourceFile);
-                    wakeup_sources = true;
-                } catch (java.io.FileNotFoundException e2) {
-                    Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
-                            sWakeupSourceFile + " exists");
-                    return null;
+                    is = new FileInputStream(sWakelockFile);
+                    wakeup_sources = false;
+                } catch (java.io.FileNotFoundException e) {
+                    try {
+                        is = new FileInputStream(sWakeupSourceFile);
+                        wakeup_sources = true;
+                    } catch (java.io.FileNotFoundException e2) {
+                        Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
+                                sWakeupSourceFile + " exists");
+                        return null;
+                    }
+                }
+
+                int cnt;
+                while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) {
+                    len += cnt;
+                }
+
+                is.close();
+            } catch (java.io.IOException e) {
+                Slog.wtf(TAG, "failed to read kernel wakelocks", e);
+                return null;
+            } finally {
+                StrictMode.setThreadPolicyMask(oldMask);
+            }
+
+            final long readTime = SystemClock.uptimeMillis() - startTime;
+            if (readTime > 100) {
+                Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms");
+            }
+
+            if (len > 0) {
+                if (len >= buffer.length) {
+                    Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+                }
+                int i;
+                for (i=0; i<len; i++) {
+                    if (buffer[i] == '\0') {
+                        len = i;
+                        break;
+                    }
                 }
             }
 
-            int cnt;
-            while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) {
-                len += cnt;
+            updateVersion(staleStats);
+            // Get native wakelock stats from SystemSuspend
+            if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+                Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
             }
-
-            is.close();
-        } catch (java.io.IOException e) {
-            Slog.wtf(TAG, "failed to read kernel wakelocks", e);
-            return null;
-        } finally {
-            StrictMode.setThreadPolicyMask(oldMask);
+            // Get kernel wakelock stats
+            parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+            return removeOldStats(staleStats);
         }
+    }
 
-        final long readTime = SystemClock.uptimeMillis() - startTime;
-        if (readTime > 100) {
-            Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms");
-        }
-
-        if (len > 0) {
-            if (len >= buffer.length) {
-                Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
-            }
-            int i;
-            for (i=0; i<len; i++) {
-                if (buffer[i] == '\0') {
-                    len = i;
-                    break;
-                }
-            }
-        }
-
-        updateVersion(staleStats);
-
-        parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
-
+    /**
+     * On success, returns the updated stats from SystemSupend, else returns null.
+     */
+    private KernelWakelockStats getWakelockStatsFromSystemSuspend(
+            final KernelWakelockStats staleStats) {
+        WakeLockInfo[] wlStats = null;
         if (mSuspendControlService == null) {
             try {
                 mSuspendControlService = ISuspendControlService.Stub.asInterface(
                     ServiceManager.getServiceOrThrow("suspend_control"));
             } catch (ServiceNotFoundException e) {
                 Slog.wtf(TAG, "Required service suspend_control not available", e);
+                return null;
             }
         }
 
         try {
-            WakeLockInfo[] wlStats = mSuspendControlService.getWakeLockStats();
-            getNativeWakelockStats(wlStats, staleStats);
+            wlStats = mSuspendControlService.getWakeLockStats();
+            updateWakelockStats(wlStats, staleStats);
         } catch (RemoteException e) {
             Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e);
+            return null;
         }
 
-        return removeOldStats(staleStats);
+        return staleStats;
     }
 
     /**
-     * Reads native wakelock stats from SystemSuspend and updates staleStats with the new
-     * information.
+     * Updates statleStats with stats from  SystemSuspend.
      * @param staleStats Existing object to update.
      * @return the updated stats.
      */
     @VisibleForTesting
-    public KernelWakelockStats getNativeWakelockStats(WakeLockInfo[] wlStats,
+    public KernelWakelockStats updateWakelockStats(WakeLockInfo[] wlStats,
                                                       final KernelWakelockStats staleStats) {
         for (WakeLockInfo info : wlStats) {
             if (!staleStats.containsKey(info.name)) {
                 staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount,
-                        info.totalTime, sKernelWakelockUpdateVersion));
+                        info.totalTime * 1000 /* ms to us */, sKernelWakelockUpdateVersion));
             } else {
                 KernelWakelockStats.Entry kwlStats = staleStats.get(info.name);
                 kwlStats.mCount = (int) info.activeCount;
-                kwlStats.mTotalTime = info.totalTime;
+                // Convert milliseconds to microseconds
+                kwlStats.mTotalTime = info.totalTime * 1000;
                 kwlStats.mVersion = sKernelWakelockUpdateVersion;
             }
         }
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index af90b15..c64103f 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -169,6 +169,11 @@
     boolean mPidQuery;
 
     /**
+     * Whether the current arguments constitute a notification that boot completed.
+     */
+    boolean mBootCompleted;
+
+    /**
      * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, or
      * when they change, via --set-api-blacklist-exemptions.
      */
@@ -330,6 +335,8 @@
                 mAbiListQuery = true;
             } else if (arg.equals("--get-pid")) {
                 mPidQuery = true;
+            } else if (arg.equals("--boot-completed")) {
+                mBootCompleted = true;
             } else if (arg.startsWith("--instruction-set=")) {
                 mInstructionSet = arg.substring(arg.indexOf('=') + 1);
             } else if (arg.startsWith("--app-data-dir=")) {
@@ -364,7 +371,11 @@
             }
         }
 
-        if (mAbiListQuery || mPidQuery) {
+        if (mBootCompleted) {
+            if (args.length - curArg > 0) {
+                throw new IllegalArgumentException("Unexpected arguments after --boot-completed");
+            }
+        } else if (mAbiListQuery || mPidQuery) {
             if (args.length - curArg > 0) {
                 throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
             }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index ad9f64c..fc25a47 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -149,6 +149,11 @@
 
         parsedArgs = new ZygoteArguments(args);
 
+        if (parsedArgs.mBootCompleted) {
+            handleBootCompleted();
+            return null;
+        }
+
         if (parsedArgs.mAbiListQuery) {
             handleAbiListQuery();
             return null;
@@ -291,6 +296,10 @@
         }
     }
 
+    private void handleBootCompleted() {
+        VMRuntime.bootCompleted();
+    }
+
     /**
      * Preloads resources if the zygote is in lazily preload mode. Writes the result of the
      * preload operation; {@code 0} when a preload was initiated due to this request and {@code 1}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 62c4d76..bbd8ffe 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -306,6 +306,8 @@
                 whichHeap = HEAP_NATIVE;
             } else if (strncmp(name, "[stack", 6) == 0) {
                 whichHeap = HEAP_STACK;
+            } else if (strncmp(name, "[anon:stack_and_tls:", 20) == 0) {
+                whichHeap = HEAP_STACK;
             } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
                 whichHeap = HEAP_SO;
                 is_swappable = true;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 614a8ff..04b0609 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -308,7 +308,8 @@
                                        buffer->getHeight(),
                                        buffer->getPixelFormat(),
                                        (jint)buffer->getUsage(),
-                                       (jlong)buffer.get());
+                                       (jlong)buffer.get(),
+                                       false /* capturedSecureLayers */);
 }
 
 static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 480b1ea..d31df38 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -12,7 +12,7 @@
 yro@google.com
 
 # Settings UI
-per-file settings_enums.proto=zhfan@google.com
+per-file settings_enums.proto=tmfang@google.com
 
 # Frameworks
 ogunwale@google.com
@@ -20,3 +20,6 @@
 
 # Launcher
 hyunyoungs@google.com
+
+# Graphics stats
+jreck@google.com
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1fce5be..72462ad4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1172,6 +1172,9 @@
 
     <bool name="config_use_strict_phone_number_comparation">false</bool>
 
+    <!-- The character count of the minimum match for comparison phone numbers -->
+    <integer name="config_phonenumber_compare_min_match">7</integer>
+
     <!-- Display low battery warning when battery level dips to this value.
          Also, the battery stats are flushed to disk when we hit this level.  -->
     <integer name="config_criticalBatteryWarningLevel">5</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e8b2b1f..9f8baf8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -290,6 +290,7 @@
   <java-symbol type="bool" name="config_ui_enableFadingMarquee" />
   <java-symbol type="bool" name="config_enableHapticTextHandle" />
   <java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
+  <java-symbol type="integer" name="config_phonenumber_compare_min_match" />
   <java-symbol type="bool" name="config_single_volume" />
   <java-symbol type="bool" name="config_voice_capable" />
   <java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index a909487..2fa4487 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -55,6 +55,8 @@
 
     resource_dirs: ["res"],
     resource_zips: [":FrameworksCoreTests_apks_as_resources"],
+
+    data: [":BstatsTestApp"],
 }
 
 // Rules to copy all the test apks to the intermediate raw resource directory
diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/core/tests/coretests/BstatsTestApp/Android.bp
index 424c71a..a89d728 100644
--- a/core/tests/coretests/BstatsTestApp/Android.bp
+++ b/core/tests/coretests/BstatsTestApp/Android.bp
@@ -15,10 +15,6 @@
 android_test_helper_app {
     name: "BstatsTestApp",
 
-    test_suites: [
-        "device-tests",
-    ],
-
     static_libs: ["coretests-aidl"],
 
     srcs: ["**/*.java"],
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index d922c16..b9f5ef9 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -412,7 +412,8 @@
                 IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
                 boolean b2, boolean b3, Configuration configuration,
                 CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1,
-                boolean autofillCompatEnabled) throws RemoteException {
+                boolean autofillCompatEnabled, long[] disableCompatChanges)
+                throws RemoteException {
         }
 
         @Override
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
index 008085e..dc9208d 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
@@ -68,12 +68,7 @@
     private WakeLockInfo createWakeLockInfo(String name, int activeCount, long totalTime) {
         WakeLockInfo info = new WakeLockInfo();
         info.name = name;
-        info.pid = 1;
         info.activeCount = activeCount;
-        info.isActive = true;
-        info.activeSince = 0;
-        info.lastChange = 0;
-        info.maxTime = 0;
         info.totalTime = totalTime;
         return info;
     }
@@ -89,7 +84,7 @@
                                                         byte[] buffer, WakeLockInfo[] wlStats) {
         mReader.updateVersion(staleStats);
         mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats);
-        mReader.getNativeWakelockStats(wlStats, staleStats);
+        mReader.updateWakelockStats(wlStats, staleStats);
         return mReader.removeOldStats(staleStats);
     }
 
@@ -101,7 +96,7 @@
         mReader = new KernelWakelockReader();
     }
 
-// ------------------------- Kernel Wakelock Stats Test ------------------------
+// ------------------------- Legacy Wakelock Stats Test ------------------------
     @SmallTest
     public void testParseEmptyFile() throws Exception {
         KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true,
@@ -196,10 +191,10 @@
         assertFalse(staleStats.containsKey("Fakelock"));
     }
 
-// -------------------- Native (SystemSuspend) Wakelock Stats Test -------------------
+// -------------------- SystemSuspend Wakelock Stats Test -------------------
     @SmallTest
     public void testEmptyWakeLockInfoList() {
-        KernelWakelockStats staleStats = mReader.getNativeWakelockStats(new WakeLockInfo[0],
+        KernelWakelockStats staleStats = mReader.updateWakelockStats(new WakeLockInfo[0],
                 new KernelWakelockStats());
 
         assertTrue(staleStats.isEmpty());
@@ -208,9 +203,9 @@
     @SmallTest
     public void testOneWakeLockInfo() {
         WakeLockInfo[] wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock", 20, 10000);
+        wlStats[0] = createWakeLockInfo("WakeLock", 20, 1000);   // Milliseconds
 
-        KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats,
+        KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats,
                 new KernelWakelockStats());
 
         assertEquals(1, staleStats.size());
@@ -219,16 +214,16 @@
 
         KernelWakelockStats.Entry entry = staleStats.get("WakeLock");
         assertEquals(20, entry.mCount);
-        assertEquals(10000, entry.mTotalTime);
+        assertEquals(1000 * 1000, entry.mTotalTime);   // Microseconds
     }
 
     @SmallTest
     public void testTwoWakeLockInfos() {
         WakeLockInfo[] wlStats = new WakeLockInfo[2];
-        wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000);
-        wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000);
+        wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
+        wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds
 
-        KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats,
+        KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats,
                 new KernelWakelockStats());
 
         assertEquals(2, staleStats.size());
@@ -238,17 +233,17 @@
 
         KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1");
         assertEquals(10, entry1.mCount);
-        assertEquals(1000, entry1.mTotalTime);
+        assertEquals(1000 * 1000, entry1.mTotalTime); // Microseconds
 
         KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
         assertEquals(20, entry2.mCount);
-        assertEquals(2000, entry2.mTotalTime);
+        assertEquals(2000 * 1000, entry2.mTotalTime); // Microseconds
     }
 
     @SmallTest
     public void testWakeLockInfosBecomeStale() {
         WakeLockInfo[] wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000);
+        wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
 
         KernelWakelockStats staleStats = new KernelWakelockStats();
 
@@ -259,9 +254,9 @@
         assertTrue(staleStats.containsKey("WakeLock1"));
         KernelWakelockStats.Entry entry = staleStats.get("WakeLock1");
         assertEquals(10, entry.mCount);
-        assertEquals(1000, entry.mTotalTime);
+        assertEquals(1000 * 1000, entry.mTotalTime);  // Microseconds
 
-        wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000);
+        wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds
 
         readKernelWakelockStats(staleStats, new byte[0], wlStats);
 
@@ -271,7 +266,7 @@
         assertTrue(staleStats.containsKey("WakeLock2"));
         entry = staleStats.get("WakeLock2");
         assertEquals(20, entry.mCount);
-        assertEquals(2000, entry.mTotalTime);
+        assertEquals(2000 * 1000, entry.mTotalTime); // Micro seconds
     }
 
 // -------------------- Aggregate  Wakelock Stats Tests --------------------
@@ -313,7 +308,7 @@
 
         byte[] buffer = new byte[0];
         WakeLockInfo[] wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000);
+        wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000);  // Milliseconds
 
         readKernelWakelockStats(staleStats, buffer, wlStats);
 
@@ -323,7 +318,7 @@
 
         KernelWakelockStats.Entry entry = staleStats.get("WakeLock");
         assertEquals(10, entry.mCount);
-        assertEquals(1000, entry.mTotalTime);
+        assertEquals(1000 * 1000, entry.mTotalTime);  // Microseconds
     }
 
     @SmallTest
@@ -334,7 +329,7 @@
                 .addLine("WakeLock1", 34, 123)  // Milliseconds
                 .getBytes();
         WakeLockInfo[] wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000);
+        wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000); // Milliseconds
 
         readKernelWakelockStats(staleStats, buffer, wlStats);
 
@@ -348,7 +343,7 @@
         assertTrue(staleStats.containsKey("WakeLock2"));
         KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
         assertEquals(10, entry2.mCount);
-        assertEquals(1000, entry2.mTotalTime);
+        assertEquals(1000 * 1000, entry2.mTotalTime);  // Microseconds
     }
 
     @SmallTest
@@ -360,8 +355,8 @@
                 .addLine("WakeLock2", 46, 345)  // Milliseconds
                 .getBytes();
         WakeLockInfo[] wlStats = new WakeLockInfo[2];
-        wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000);
-        wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000);
+        wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000); // Milliseconds
+        wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000); // Milliseconds
 
         readKernelWakelockStats(staleStats, buffer, wlStats);
 
@@ -382,18 +377,18 @@
 
         KernelWakelockStats.Entry entry3 = staleStats.get("WakeLock3");
         assertEquals(10, entry3.mCount);
-        assertEquals(1000, entry3.mTotalTime);
+        assertEquals(1000 * 1000, entry3.mTotalTime); // Microseconds
 
         KernelWakelockStats.Entry entry4 = staleStats.get("WakeLock4");
         assertEquals(20, entry4.mCount);
-        assertEquals(2000, entry4.mTotalTime);
+        assertEquals(2000 * 1000, entry4.mTotalTime); // Microseconds
 
         buffer = new ProcFileBuilder()
                 .addLine("WakeLock1", 45, 789)  // Milliseconds
                 .addLine("WakeLock1", 56, 123)  // Milliseconds
                 .getBytes();
         wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000);
+        wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000); // Milliseconds
 
         readKernelWakelockStats(staleStats, buffer, wlStats);
 
@@ -411,6 +406,6 @@
 
         entry2 = staleStats.get("WakeLock4");
         assertEquals(40, entry2.mCount);
-        assertEquals(4000, entry4.mTotalTime);
+        assertEquals(4000 * 1000, entry4.mTotalTime); // Microseconds
     }
 }
diff --git a/media/OWNERS b/media/OWNERS
index 72c8952..0a12adc 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -9,6 +9,7 @@
 jaewan@google.com
 jmtrivi@google.com
 jsharkey@android.com
+klhyun@google.com
 lajos@google.com
 marcone@google.com
 sungsoo@google.com
diff --git a/media/java/android/media/AudioPortConfig.java b/media/java/android/media/AudioPortConfig.java
index 45e49a7..ac19bb1 100644
--- a/media/java/android/media/AudioPortConfig.java
+++ b/media/java/android/media/AudioPortConfig.java
@@ -95,7 +95,6 @@
 
     /**
      * The gain configuration if this port supports gain control, null otherwise
-     * @see AudioGainConfig.
      */
     public AudioGainConfig gain() {
         return mGain;
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index f9a4b1e..6d9d626 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -19,10 +19,12 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.Looper;
 import android.os.Message;
-import java.util.ArrayList;
+
+import com.android.internal.annotations.GuardedBy;
+
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 
 /**
  * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
@@ -33,6 +35,9 @@
 class AudioPortEventHandler {
     private Handler mHandler;
     private HandlerThread mHandlerThread;
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
     private final ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners =
             new ArrayList<AudioManager.OnAudioPortUpdateListener>();
 
@@ -53,7 +58,7 @@
     private long mJniCallback;
 
     void init() {
-        synchronized (this) {
+        synchronized (mLock) {
             if (mHandler != null) {
                 return;
             }
@@ -66,7 +71,7 @@
                     @Override
                     public void handleMessage(Message msg) {
                         ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
-                        synchronized (this) {
+                        synchronized (mLock) {
                             if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
                                 listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
                                 if (mListeners.contains(msg.obj)) {
@@ -152,7 +157,7 @@
     private native void native_finalize();
 
     void registerListener(AudioManager.OnAudioPortUpdateListener l) {
-        synchronized (this) {
+        synchronized (mLock) {
             mListeners.add(l);
         }
         if (mHandler != null) {
@@ -162,7 +167,7 @@
     }
 
     void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
-        synchronized (this) {
+        synchronized (mLock) {
             mListeners.remove(l);
         }
     }
diff --git a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
index d06e5ec..f10a3ac 100644
--- a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
+++ b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
@@ -63,7 +63,7 @@
     }
 
     override fun draw(c: Canvas) {
-        c?.let {
+        c.let {
             val w = bounds.width().toFloat()
             val h = bounds.height().toFloat()
             val inset = _size / 12 // 2dp in a 24x24 icon
diff --git a/packages/InputDevices/Android.bp b/packages/InputDevices/Android.bp
new file mode 100644
index 0000000..7532aea
--- /dev/null
+++ b/packages/InputDevices/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2012 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.
+
+android_app {
+    name: "InputDevices",
+
+    srcs: [
+        "**/*.java",
+        ":validate_input_devices_keymaps",
+    ],
+
+    resource_dirs: ["res"],
+
+    sdk_version: "current",
+    certificate: "platform",
+    privileged: true,
+}
+
+// Validate all key maps.
+// Produces an empty srcjar that is used as an input to InputDevices to make sure
+// the check runs for platform builds.
+genrule {
+    name: "validate_input_devices_keymaps",
+    tools: [
+        "validatekeymaps",
+        "soong_zip",
+    ],
+    srcs: ["res/raw/*.kcm"],
+    out: ["validate_input_devices_keymaps.srcjar"],
+    cmd: "$(location validatekeymaps) -q $(in) && $(location soong_zip) -o $(out)",
+}
diff --git a/packages/InputDevices/Android.mk b/packages/InputDevices/Android.mk
deleted file mode 100644
index 6de1f1d..0000000
--- a/packages/InputDevices/Android.mk
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2012 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES := 
-
-LOCAL_PACKAGE_NAME := InputDevices
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
-
-# Validate all key maps.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := validate_input_devices_keymaps
-intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON)
-LOCAL_BUILT_MODULE := $(intermediates)/stamp
-
-validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
-input_devices_keymaps := $(wildcard $(LOCAL_PATH)/res/raw/*.kcm)
-$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps)
-$(LOCAL_BUILT_MODULE) : $(input_devices_keymaps) | $(validatekeymaps)
-	$(hide) $(PRIVATE_VALIDATEKEYMAPS) $^
-	$(hide) mkdir -p $(dir $@) && touch $@
-
-# Run validatekeymaps unconditionally for platform build.
-droidcore : $(LOCAL_BUILT_MODULE)
-
-# Reset temp vars.
-validatekeymaps :=
-input_devices_keymaps :=
diff --git a/packages/MtpDocumentsProvider/Android.bp b/packages/MtpDocumentsProvider/Android.bp
new file mode 100644
index 0000000..3dafa26
--- /dev/null
+++ b/packages/MtpDocumentsProvider/Android.bp
@@ -0,0 +1,11 @@
+android_app {
+    name: "MtpDocumentsProvider",
+
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "media",
+    privileged: true,
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+}
diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk
deleted file mode 100644
index 2d62a07..0000000
--- a/packages/MtpDocumentsProvider/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := MtpDocumentsProvider
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := media
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-# Only enable asserts on userdebug/eng builds
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
-LOCAL_JACK_FLAGS += -D jack.assert.policy=always
-endif
-
-include $(BUILD_PACKAGE)
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 8428797..53ccd1b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -112,7 +112,6 @@
     <uses-permission android:name="android.permission.BIND_APPWIDGET" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
-    <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
     <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1060c7b..3750a8a 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -175,9 +175,9 @@
     // Passed to Message.obtain() when msg.arg2 is not used.
     private static final int UNUSED_ARG2 = -2;
 
-    // Maximum progress displayed (like 99.00%).
-    private static final int CAPPED_PROGRESS = 9900;
-    private static final int CAPPED_MAX = 10000;
+    // Maximum progress displayed in %.
+    private static final int CAPPED_PROGRESS = 99;
+    private static final int CAPPED_MAX = 100;
 
     /** Show the progress log every this percent. */
     private static final int LOG_PROGRESS_STEP = 10;
@@ -1954,7 +1954,10 @@
 
         @Override
         public void onProgress(int progress) throws RemoteException {
-            updateProgressInfo(progress, 100 /* progress is already a percentage; so max = 100 */);
+            if (progress > CAPPED_PROGRESS) {
+                progress = CAPPED_PROGRESS;
+            }
+            updateProgressInfo(progress, CAPPED_MAX);
         }
 
         @Override
@@ -1967,46 +1970,6 @@
             // TODO(b/111441001): implement
         }
 
-        @Override
-        public void onProgressUpdated(int progress) throws RemoteException {
-            /*
-             * Checks whether the progress changed in a way that should be displayed to the user:
-             * - info.progress / info.max represents the displayed progress
-             * - info.realProgress / info.realMax represents the real progress
-             * - since the real progress can decrease, the displayed progress is only updated if it
-             *   increases
-             * - the displayed progress is capped at a maximum (like 99%)
-             */
-            info.realProgress = progress;
-            final int oldPercentage = (CAPPED_MAX * info.progress) / info.max;
-            int newPercentage = (CAPPED_MAX * info.realProgress) / info.realMax;
-            int max = info.realMax;
-
-            if (newPercentage > CAPPED_PROGRESS) {
-                progress = newPercentage = CAPPED_PROGRESS;
-                max = CAPPED_MAX;
-            }
-
-            if (newPercentage > oldPercentage) {
-                updateProgressInfo(progress, max);
-            }
-        }
-
-        @Override
-        public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
-            Log.d(TAG, "onMaxProgressUpdated: " + maxProgress);
-            info.realMax = maxProgress;
-        }
-
-        @Override
-        public void onSectionComplete(String title, int status, int size, int durationMs)
-                throws RemoteException {
-            if (DEBUG) {
-                Log.v(TAG, "Title: " + title + " Status: " + status + " Size: " + size
-                        + " Duration: " + durationMs + "ms");
-            }
-        }
-
         public void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("token: "); pw.println(token);
         }
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
new file mode 100644
index 0000000..d91a116
--- /dev/null
+++ b/packages/SimAppDialog/Android.bp
@@ -0,0 +1,15 @@
+android_app {
+    name: "SimAppDialog",
+
+    srcs: ["src/**/*.java"],
+
+    platform_apis: true,
+    certificate: "platform",
+
+    static_libs: [
+        "android-support-v4",
+        "setup-wizard-lib",
+    ],
+
+    resource_dirs: ["res"],
+}
diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk
deleted file mode 100644
index 6a4099b..0000000
--- a/packages/SimAppDialog/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := SimAppDialog
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    android-support-v4
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-include frameworks/opt/setupwizard/library/common-platform-deprecated.mk
-
-include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/plugin/ExamplePlugin/Android.bp b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
index a0eaf14..c6c80f3 100644
--- a/packages/SystemUI/plugin/ExamplePlugin/Android.bp
+++ b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
@@ -11,4 +11,5 @@
 
     srcs: ["src/**/*.java"],
 
+    platform_apis: true,
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 03fb9bb..0fcb994 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -340,7 +340,7 @@
                 case SimPuk:
                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
                     SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
-                    if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled(
+                    if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser())) {
                         finish = true;
                     } else {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 38a90cf..c906240 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -219,7 +219,7 @@
         intent.setComponent(assistComponent);
         intent.putExtras(args);
 
-        if (structureEnabled) {
+        if (structureEnabled && AssistUtils.isDisclosureEnabled(mContext)) {
             showDisclosure();
         }
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5ddb687..45584aa 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -12,12 +12,14 @@
     },
     srcs: [
         "java/**/*.java",
+        ":platformcompat_aidl",
         ":dumpstate_aidl",
         ":installd_aidl",
         ":storaged_aidl",
         ":vold_aidl",
         ":gsiservice_aidl",
         ":mediaupdateservice_aidl",
+        ":platform-compat-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/policy/EventLogTags.logtags",
@@ -78,3 +80,11 @@
     name: "gps_debug.conf",
     src: "java/com/android/server/location/gps_debug.conf",
 }
+
+filegroup {
+    name: "platformcompat_aidl",
+    srcs: [
+        "java/com/android/server/compat/IPlatformCompat.aidl",
+    ],
+    path: "java",
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 223eb55..89b59cf 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1142,7 +1142,8 @@
         if (isBluetoothDisallowed) {
             return;
         }
-        if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
+        final boolean isSafeMode = mContext.getPackageManager().isSafeMode();
+        if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) {
             if (DBG) {
                 Slog.d(TAG, "Auto-enabling Bluetooth.");
             }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 231c015..667445b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -149,7 +149,6 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.Xml;
 
@@ -167,7 +166,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.MessageUtils;
-import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.AutodestructReference;
@@ -304,7 +302,8 @@
     /** Flag indicating if background data is restricted. */
     private boolean mRestrictBackground;
 
-    final private Context mContext;
+    private final Context mContext;
+    private final Dependencies mDeps;
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
 
@@ -585,11 +584,6 @@
     private NetworkNotificationManager mNotifier;
     private LingerMonitor mLingerMonitor;
 
-    // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
-    private static final int MIN_NET_ID = 100; // some reserved marks
-    private static final int MAX_NET_ID = 65535 - 0x0400; // Top 1024 bits reserved by IpSecService
-    private int mNextNetId = MIN_NET_ID;
-
     // sequence number of NetworkRequests
     private int mNextNetworkRequestId = 1;
 
@@ -833,19 +827,113 @@
         }
     };
 
+    /**
+     * Dependencies of ConnectivityService, for injection in tests.
+     */
+    @VisibleForTesting
+    public static class Dependencies {
+        /**
+         * Get system properties to use in ConnectivityService.
+         */
+        public MockableSystemProperties getSystemProperties() {
+            return new MockableSystemProperties();
+        }
+
+        /**
+         * Create a HandlerThread to use in ConnectivityService.
+         */
+        public HandlerThread makeHandlerThread() {
+            return new HandlerThread("ConnectivityServiceThread");
+        }
+
+        /**
+         * Get a reference to the NetworkStackClient.
+         */
+        public NetworkStackClient getNetworkStack() {
+            return NetworkStackClient.getInstance();
+        }
+
+        /**
+         * @see Tethering
+         */
+        public Tethering makeTethering(@NonNull Context context,
+                @NonNull INetworkManagementService nms,
+                @NonNull INetworkStatsService statsService,
+                @NonNull INetworkPolicyManager policyManager,
+                @NonNull TetheringDependencies tetheringDeps) {
+            return new Tethering(context, nms, statsService, policyManager,
+                    IoThread.get().getLooper(), getSystemProperties(), tetheringDeps);
+        }
+
+        /**
+         * @see ProxyTracker
+         */
+        public ProxyTracker makeProxyTracker(@NonNull Context context,
+                @NonNull Handler connServiceHandler) {
+            return new ProxyTracker(context, connServiceHandler, EVENT_PROXY_HAS_CHANGED);
+        }
+
+        /**
+         * @see NetIdManager
+         */
+        public NetIdManager makeNetIdManager() {
+            return new NetIdManager();
+        }
+
+        /**
+         * @see NetworkUtils#queryUserAccess(int, int)
+         */
+        public boolean queryUserAccess(int uid, int netId) {
+            return NetworkUtils.queryUserAccess(uid, netId);
+        }
+
+        /**
+         * @see MultinetworkPolicyTracker
+         */
+        public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(
+                @NonNull Context c, @NonNull Handler h, @NonNull Runnable r) {
+            return new MultinetworkPolicyTracker(c, h, r);
+        }
+
+        /**
+         * @see ServiceManager#checkService(String)
+         */
+        public boolean hasService(@NonNull String name) {
+            return ServiceManager.checkService(name) != null;
+        }
+
+        /**
+         * @see IpConnectivityMetrics.Logger
+         */
+        public IpConnectivityMetrics.Logger getMetricsLogger() {
+            return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
+                    "no IpConnectivityMetrics service");
+        }
+
+        /**
+         * @see IpConnectivityMetrics
+         */
+        public IIpConnectivityMetrics getIpConnectivityMetrics() {
+            return IIpConnectivityMetrics.Stub.asInterface(
+                    ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+        }
+    }
+
     public ConnectivityService(Context context, INetworkManagementService netManager,
             INetworkStatsService statsService, INetworkPolicyManager policyManager) {
-        this(context, netManager, statsService, policyManager,
-            getDnsResolver(), new IpConnectivityLog(), NetdService.getInstance());
+        this(context, netManager, statsService, policyManager, getDnsResolver(),
+                new IpConnectivityLog(), NetdService.getInstance(), new Dependencies());
     }
 
     @VisibleForTesting
     protected ConnectivityService(Context context, INetworkManagementService netManager,
             INetworkStatsService statsService, INetworkPolicyManager policyManager,
-            IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd) {
+            IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) {
         if (DBG) log("ConnectivityService starting up");
 
-        mSystemProperties = getSystemProperties();
+        mDeps = checkNotNull(deps, "missing Dependencies");
+        mSystemProperties = mDeps.getSystemProperties();
+        mNetIdManager = mDeps.makeNetIdManager();
 
         mMetricsLog = logger;
         mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
@@ -862,7 +950,7 @@
         mDefaultWifiRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
 
-        mHandlerThread = new HandlerThread("ConnectivityServiceThread");
+        mHandlerThread = mDeps.makeHandlerThread();
         mHandlerThread.start();
         mHandler = new InternalHandler(mHandlerThread.getLooper());
         mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
@@ -880,7 +968,7 @@
                 LocalServices.getService(NetworkPolicyManagerInternal.class),
                 "missing NetworkPolicyManagerInternal");
         mDnsResolver = checkNotNull(dnsresolver, "missing IDnsResolver");
-        mProxyTracker = makeProxyTracker();
+        mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
 
         mNetd = netd;
         mKeyStore = KeyStore.getInstance();
@@ -948,7 +1036,7 @@
 
         // Do the same for Ethernet, since it's often not specified in the configs, although many
         // devices can use it via USB host adapters.
-        if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) {
+        if (mNetConfigs[TYPE_ETHERNET] == null && mDeps.hasService(Context.ETHERNET_SERVICE)) {
             mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
             mNetworksDefined++;
         }
@@ -968,7 +1056,8 @@
 
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
-        mTethering = makeTethering();
+        mTethering = deps.makeTethering(mContext, mNMS, mStatsService, mPolicyManager,
+                makeTetheringDependencies());
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
 
@@ -1027,7 +1116,7 @@
                 LingerMonitor.DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS);
         mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit);
 
-        mMultinetworkPolicyTracker = createMultinetworkPolicyTracker(
+        mMultinetworkPolicyTracker = mDeps.makeMultinetworkPolicyTracker(
                 mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
         mMultinetworkPolicyTracker.start();
 
@@ -1037,10 +1126,8 @@
         registerPrivateDnsSettingsCallbacks();
     }
 
-    @VisibleForTesting
-    protected Tethering makeTethering() {
-        // TODO: Move other elements into @Overridden getters.
-        final TetheringDependencies deps = new TetheringDependencies() {
+    private TetheringDependencies makeTetheringDependencies() {
+        return new TetheringDependencies() {
             @Override
             public boolean isTetheringSupported() {
                 return ConnectivityService.this.isTetheringSupported();
@@ -1050,14 +1137,6 @@
                 return mDefaultRequest;
             }
         };
-        return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
-                IoThread.get().getLooper(), new MockableSystemProperties(),
-                deps);
-    }
-
-    @VisibleForTesting
-    protected ProxyTracker makeProxyTracker() {
-        return new ProxyTracker(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
     }
 
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1149,22 +1228,6 @@
         return mNextNetworkRequestId++;
     }
 
-    @VisibleForTesting
-    protected int reserveNetId() {
-        synchronized (mNetworkForNetId) {
-            for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
-                int netId = mNextNetId;
-                if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
-                // Make sure NetID unused.  http://b/16815182
-                if (!mNetIdInUse.get(netId)) {
-                    mNetIdInUse.put(netId, true);
-                    return netId;
-                }
-            }
-        }
-        throw new IllegalStateException("No free netIds");
-    }
-
     private NetworkState getFilteredNetworkState(int networkType, int uid) {
         if (mLegacyTypeTracker.isTypeSupported(networkType)) {
             final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
@@ -1796,11 +1859,8 @@
         }
     };
 
-    @VisibleForTesting
-    protected void registerNetdEventCallback() {
-        final IIpConnectivityMetrics ipConnectivityMetrics =
-                IIpConnectivityMetrics.Stub.asInterface(
-                        ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+    private void registerNetdEventCallback() {
+        final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics();
         if (ipConnectivityMetrics == null) {
             Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
             return;
@@ -2236,12 +2296,6 @@
     protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
     private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
 
-    // Overridden for testing purposes to avoid writing to SystemProperties.
-    @VisibleForTesting
-    protected MockableSystemProperties getSystemProperties() {
-        return new MockableSystemProperties();
-    }
-
     private void updateTcpBufferSizes(String tcpBufferSizes) {
         String[] values = null;
         if (tcpBufferSizes != null) {
@@ -2631,8 +2685,9 @@
                     }
                     if (valid != nai.lastValidated) {
                         if (wasDefault) {
-                            metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
-                                    SystemClock.elapsedRealtime(), valid);
+                            mDeps.getMetricsLogger()
+                                    .defaultNetworkMetrics().logDefaultNetworkValidity(
+                                            SystemClock.elapsedRealtime(), valid);
                         }
                         final int oldScore = nai.getCurrentScore();
                         nai.lastValidated = valid;
@@ -2967,8 +3022,8 @@
                     final boolean wasDefault = isDefaultNetwork(nai);
                     synchronized (mNetworkForNetId) {
                         mNetworkForNetId.remove(nai.network.netId);
-                        mNetIdInUse.delete(nai.network.netId);
                     }
+                    mNetIdManager.releaseNetId(nai.network.netId);
                     // Just in case.
                     mLegacyTypeTracker.remove(nai, wasDefault);
                 }
@@ -3015,7 +3070,7 @@
             // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
             // whose timestamps tell how long it takes to recover a default network.
             long now = SystemClock.elapsedRealtime();
-            metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
+            mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
         }
         notifyIfacesChangedForNetworkStats();
         // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -3069,9 +3124,7 @@
             destroyNativeNetwork(nai);
             mDnsManager.removeNetwork(nai.network);
         }
-        synchronized (mNetworkForNetId) {
-            mNetIdInUse.delete(nai.network.netId);
-        }
+        mNetIdManager.releaseNetId(nai.network.netId);
     }
 
     private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
@@ -4155,7 +4208,7 @@
                 return null;
             }
             return getLinkPropertiesProxyInfo(activeNetwork);
-        } else if (queryUserAccess(Binder.getCallingUid(), network.netId)) {
+        } else if (mDeps.queryUserAccess(Binder.getCallingUid(), network.netId)) {
             // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
             // caller may not have.
             return getLinkPropertiesProxyInfo(network);
@@ -4164,10 +4217,6 @@
         return null;
     }
 
-    @VisibleForTesting
-    protected boolean queryUserAccess(int uid, int netId) {
-        return NetworkUtils.queryUserAccess(uid, netId);
-    }
 
     private ProxyInfo getLinkPropertiesProxyInfo(Network network) {
         final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
@@ -4760,7 +4809,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             // Concatenate the range of types onto the range of NetIDs.
-            int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
+            int id = NetIdManager.MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
             mNotifier.setProvNotificationVisible(visible, id, action);
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -5371,10 +5420,9 @@
     @GuardedBy("mNetworkForNetId")
     private final SparseArray<NetworkAgentInfo> mNetworkForNetId = new SparseArray<>();
     // NOTE: Accessed on multiple threads, synchronized with mNetworkForNetId.
-    // An entry is first added to mNetIdInUse, prior to mNetworkForNetId, so
+    // An entry is first reserved with NetIdManager, prior to being added to mNetworkForNetId, so
     // there may not be a strict 1:1 correlation between the two.
-    @GuardedBy("mNetworkForNetId")
-    private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
+    private final NetIdManager mNetIdManager;
 
     // NetworkAgentInfo keyed off its connecting messenger
     // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
@@ -5476,9 +5524,9 @@
         // satisfies mDefaultRequest.
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
-                new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
-                mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mDnsResolver,
-                mNMS, factorySerialNumber);
+                new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
+                currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
+                mDnsResolver, mNMS, factorySerialNumber);
         // Make sure the network capabilities reflect what the agent info says.
         nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
         final String extraInfo = networkInfo.getExtraInfo();
@@ -5487,7 +5535,7 @@
         if (DBG) log("registerNetworkAgent " + nai);
         final long token = Binder.clearCallingIdentity();
         try {
-            getNetworkStack().makeNetworkMonitor(
+            mDeps.getNetworkStack().makeNetworkMonitor(
                     nai.network, name, new NetworkMonitorCallbacks(nai));
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -5499,11 +5547,6 @@
         return nai.network.netId;
     }
 
-    @VisibleForTesting
-    protected NetworkStackClient getNetworkStack() {
-        return NetworkStackClient.getInstance();
-    }
-
     private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
         nai.onNetworkMonitorCreated(networkMonitor);
         if (VDBG) log("Got NetworkAgent Messenger");
@@ -5519,7 +5562,6 @@
         }
         nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger);
         NetworkInfo networkInfo = nai.networkInfo;
-        nai.networkInfo = null;
         updateNetworkInfo(nai, networkInfo);
         updateUids(nai, null, nai.networkCapabilities);
     }
@@ -6313,7 +6355,7 @@
             // Notify system services that this network is up.
             makeDefault(newNetwork);
             // Log 0 -> X and Y -> X default network transitions, where X is the new default.
-            metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
+            mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
                     now, newNetwork, oldDefaultNetwork);
             // Have a new default network, release the transition wakelock in
             scheduleReleaseNetworkTransitionWakelock();
@@ -6518,8 +6560,7 @@
 
         if (DBG) {
             log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
-                    (oldInfo == null ? "null" : oldInfo.getState()) +
-                    " to " + state);
+                    oldInfo.getState() + " to " + state);
         }
 
         if (!networkAgent.created
@@ -6592,8 +6633,8 @@
                 // TODO(b/122649188): send the broadcast only to VPN users.
                 mProxyTracker.sendProxyBroadcast();
             }
-        } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
-                state == NetworkInfo.State.SUSPENDED) {
+        } else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED ||
+                state == NetworkInfo.State.SUSPENDED)) {
             // going into or coming out of SUSPEND: re-score and notify
             if (networkAgent.getCurrentScore() != oldScore) {
                 rematchAllNetworksAndRequests(networkAgent, oldScore);
@@ -6985,27 +7026,6 @@
         return nwm.getWatchlistConfigHash();
     }
 
-    @VisibleForTesting
-    MultinetworkPolicyTracker createMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
-        return new MultinetworkPolicyTracker(c, h, r);
-    }
-
-    @VisibleForTesting
-    public WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int cmd, Object obj) {
-        return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
-    }
-
-    @VisibleForTesting
-    public boolean hasService(String name) {
-        return ServiceManager.checkService(name) != null;
-    }
-
-    @VisibleForTesting
-    protected IpConnectivityMetrics.Logger metricsLogger() {
-        return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
-                "no IpConnectivityMetrics service");
-    }
-
     private void logNetworkEvent(NetworkAgentInfo nai, int evtype) {
         int[] transports = nai.networkCapabilities.getTransportTypes();
         mMetricsLog.log(nai.network.netId, transports, new NetworkEvent(evtype));
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 4639d75..70569db 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -191,7 +191,7 @@
             if (!file.getFileDescriptor().valid()) {
                 throw new IllegalStateException("Invalid file descriptor");
             }
-            return new ParcelFileDescriptor(file.getFileDescriptor());
+            return ParcelFileDescriptor.dup(file.getFileDescriptor());
         } catch (IOException ex) {
             throw new IllegalStateException("Failed to get PFD from memory file", ex);
         }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index b4e1c32..a0946a0 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -750,10 +750,10 @@
         }
     }
 
-    // These values have been reserved in ConnectivityService
+    // These values have been reserved in NetIdManager
     @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
 
-    @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
+    public static final int TUN_INTF_NETID_RANGE = 0x0400;
 
     private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
     private int mNextTunnelNetIdIndex = 0;
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
new file mode 100644
index 0000000..11533be
--- /dev/null
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Class used to reserve and release net IDs.
+ *
+ * <p>Instances of this class are thread-safe.
+ */
+public class NetIdManager {
+    // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
+    public static final int MIN_NET_ID = 100; // some reserved marks
+    // Top IDs reserved by IpSecService
+    public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE;
+
+    @GuardedBy("mNetIdInUse")
+    private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
+
+    @GuardedBy("mNetIdInUse")
+    private int mLastNetId = MIN_NET_ID - 1;
+
+    /**
+     * Get the first netId that follows the provided lastId and is available.
+     */
+    private static int getNextAvailableNetIdLocked(
+            int lastId, @NonNull SparseBooleanArray netIdInUse) {
+        int netId = lastId;
+        for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
+            netId = netId < MAX_NET_ID ? netId + 1 : MIN_NET_ID;
+            if (!netIdInUse.get(netId)) {
+                return netId;
+            }
+        }
+        throw new IllegalStateException("No free netIds");
+    }
+
+    /**
+     * Reserve a new ID for a network.
+     */
+    public int reserveNetId() {
+        synchronized (mNetIdInUse) {
+            mLastNetId = getNextAvailableNetIdLocked(mLastNetId, mNetIdInUse);
+            // Make sure NetID unused.  http://b/16815182
+            mNetIdInUse.put(mLastNetId, true);
+            return mLastNetId;
+        }
+    }
+
+    /**
+     * Clear a previously reserved ID for a network.
+     */
+    public void releaseNetId(int id) {
+        synchronized (mNetIdInUse) {
+            mNetIdInUse.delete(id);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 371e517..d46758c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1452,10 +1452,11 @@
     }
 
     private void start() {
-        connect();
+        connectStoraged();
+        connectVold();
     }
 
-    private void connect() {
+    private void connectStoraged() {
         IBinder binder = ServiceManager.getService("storaged");
         if (binder != null) {
             try {
@@ -1464,7 +1465,7 @@
                     public void binderDied() {
                         Slog.w(TAG, "storaged died; reconnecting");
                         mStoraged = null;
-                        connect();
+                        connectStoraged();
                     }
                 }, 0);
             } catch (RemoteException e) {
@@ -1478,7 +1479,17 @@
             Slog.w(TAG, "storaged not found; trying again");
         }
 
-        binder = ServiceManager.getService("vold");
+        if (mStoraged == null) {
+            BackgroundThread.getHandler().postDelayed(() -> {
+                connectStoraged();
+            }, DateUtils.SECOND_IN_MILLIS);
+        } else {
+            onDaemonConnected();
+        }
+    }
+
+    private void connectVold() {
+        IBinder binder = ServiceManager.getService("vold");
         if (binder != null) {
             try {
                 binder.linkToDeath(new DeathRecipient() {
@@ -1486,7 +1497,7 @@
                     public void binderDied() {
                         Slog.w(TAG, "vold died; reconnecting");
                         mVold = null;
-                        connect();
+                        connectVold();
                     }
                 }, 0);
             } catch (RemoteException e) {
@@ -1506,9 +1517,9 @@
             Slog.w(TAG, "vold not found; trying again");
         }
 
-        if (mStoraged == null || mVold == null) {
+        if (mVold == null) {
             BackgroundThread.getHandler().postDelayed(() -> {
-                connect();
+                connectVold();
             }, DateUtils.SECOND_IN_MILLIS);
         } else {
             onDaemonConnected();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4eafe7c..78b2914 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -452,20 +452,10 @@
 import com.android.server.SystemServiceManager;
 import com.android.server.ThreadPriorityBooster;
 import com.android.server.Watchdog;
-import com.android.server.am.ActivityManagerServiceDumpActivitiesProto;
-import com.android.server.am.ActivityManagerServiceDumpBroadcastsProto;
-import com.android.server.am.ActivityManagerServiceDumpProcessesProto;
 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
-import com.android.server.am.ActivityManagerServiceDumpServicesProto;
 import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.GrantUriProto;
-import com.android.server.am.ImportanceTokenProto;
-import com.android.server.am.MemInfoDumpProto;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
-import com.android.server.am.NeededUriGrantsProto;
-import com.android.server.am.ProcessOomProto;
-import com.android.server.am.ProcessToGcProto;
-import com.android.server.am.StickyBroadcastProto;
+import com.android.server.compat.CompatConfig;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
@@ -478,12 +468,12 @@
 
 import dalvik.system.VMRuntime;
 
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -7673,6 +7663,7 @@
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
             bindApplicationTimeMillis = SystemClock.elapsedRealtime();
             mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
+            long[] disabledCompatChanges = CompatConfig.get().getDisabledChanges(app.info);
             if (app.isolatedEntryPoint != null) {
                 // This is an isolated process which should just call an entry point instead of
                 // being bound to an application.
@@ -7688,7 +7679,7 @@
                         new Configuration(getGlobalConfiguration()), app.compat,
                         getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
-                        buildSerial, isAutofillCompatEnabled);
+                        buildSerial, isAutofillCompatEnabled, disabledCompatChanges);
             } else {
                 thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                         null, null, null, testMode,
@@ -7697,7 +7688,7 @@
                         new Configuration(getGlobalConfiguration()), app.compat,
                         getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
-                        buildSerial, isAutofillCompatEnabled);
+                        buildSerial, isAutofillCompatEnabled, disabledCompatChanges);
             }
             if (profilerInfo != null) {
                 profilerInfo.closeFd();
@@ -7907,6 +7898,10 @@
             }
         }
 
+        // Let the ART runtime in zygote and system_server know that the boot completed.
+        ZYGOTE_PROCESS.bootCompleted();
+        VMRuntime.bootCompleted();
+
         IntentFilter pkgFilter = new IntentFilter();
         pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
         pkgFilter.addDataScheme("package");
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 3399a76..6596cff 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -16,6 +16,15 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
+import static android.app.ActivityManager.RESIZE_MODE_USER;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
@@ -74,6 +83,7 @@
 import com.android.internal.util.HexDump;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
+import com.android.server.compat.CompatConfig;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -96,15 +106,6 @@
 import javax.microedition.khronos.egl.EGLDisplay;
 import javax.microedition.khronos.egl.EGLSurface;
 
-import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityManager.RESIZE_MODE_USER;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-
 final class ActivityManagerShellCommand extends ShellCommand {
     public static final String NO_CLASS_ERROR_CODE = "Error type 3";
     private static final String SHELL_PACKAGE_NAME = "com.android.shell";
@@ -277,6 +278,8 @@
                     return runNoHomeScreen(pw);
                 case "wait-for-broadcast-idle":
                     return runWaitForBroadcastIdle(pw);
+                case "compat":
+                    return runCompat(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -2794,6 +2797,50 @@
         return 0;
     }
 
+    private int runCompat(PrintWriter pw) {
+        final CompatConfig config = CompatConfig.get();
+        String toggleValue = getNextArgRequired();
+        long changeId;
+        String changeIdString = getNextArgRequired();
+        try {
+            changeId = Long.parseLong(changeIdString);
+        } catch (NumberFormatException e) {
+            changeId = config.lookupChangeId(changeIdString);
+        }
+        if (changeId == -1) {
+            pw.println("Unknown or invalid change: '" + changeIdString + "'.");
+        }
+        String packageName = getNextArgRequired();
+        switch(toggleValue) {
+            case "enable":
+                if (!config.addOverride(changeId, packageName, true)) {
+                    pw.println("Warning! Change " + changeId + " is not known yet. Enabling it"
+                            + " could have no effect.");
+                }
+                pw.println("Enabled change " + changeId + " for " + packageName + ".");
+                return 0;
+            case "disable":
+                if (!config.addOverride(changeId, packageName, false)) {
+                    pw.println("Warning! Change " + changeId + " is not known yet. Disabling it"
+                            + " could have no effect.");
+                }
+                pw.println("Disabled change " + changeId + " for " + packageName + ".");
+                return 0;
+            case "reset":
+                if (config.removeOverride(changeId, packageName)) {
+                    pw.println("Reset change " + changeId + " for " + packageName
+                            + " to default value.");
+                } else {
+                    pw.println("No override exists for changeId " + changeId + ".");
+                }
+                return 0;
+            default:
+                pw.println("Invalid toggle value: '" + toggleValue + "'.");
+        }
+        return -1;
+    }
+
+
     private Resources getResources(PrintWriter pw) throws RemoteException {
         // system resources does not contain all the device configuration, construct it manually.
         Configuration config = mInterface.getConfiguration();
@@ -3090,6 +3137,8 @@
             pw.println("      without restarting any processes.");
             pw.println("  write");
             pw.println("      Write all pending state to storage.");
+            pw.println("  compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
+            pw.println("      Toggles a change either by id or by name for <PACKAGE_NAME>.");
             pw.println();
             Intent.printIntentArgsHelp(pw, "");
         }
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index bb3b9be..6f32bee 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -20,6 +20,8 @@
 import android.compat.annotation.EnabledAfter;
 import android.content.pm.ApplicationInfo;
 
+import com.android.server.compat.config.Change;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -60,6 +62,16 @@
         mDisabled = disabled;
     }
 
+    /**
+     * @param change an object generated by services/core/xsd/platform-compat-config.xsd
+     */
+    public CompatChange(Change change) {
+        mChangeId = change.getId();
+        mName = change.getName();
+        mEnableAfterTargetSdk = change.getEnableAfterTargetSdk();
+        mDisabled = change.getDisabled();
+    }
+
     long getId() {
         return mChangeId;
     }
@@ -106,6 +118,12 @@
      * @return {@code true} if the change should be enabled for the package.
      */
     boolean isEnabled(ApplicationInfo app) {
+        if (app.isSystemApp()) {
+            // All changes are enabled for system apps, and we do not support overrides.
+            // Compatibility issues for system apps should be addressed in the app itself when
+            // the compatibility change is made.
+            return true;
+        }
         if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) {
             return mPackageOverrides.get(app.packageName);
         }
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index fea5d83..044e417 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -17,13 +17,27 @@
 package com.android.server.compat;
 
 import android.content.pm.ApplicationInfo;
+import android.os.Environment;
 import android.text.TextUtils;
 import android.util.LongArray;
 import android.util.LongSparseArray;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.compat.config.Change;
+import com.android.server.compat.config.XmlParser;
 
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+
+import javax.xml.datatype.DatatypeConfigurationException;
 /**
  * This class maintains state relating to platform compatibility changes.
  *
@@ -32,7 +46,12 @@
  */
 public final class CompatConfig {
 
-    private static final CompatConfig sInstance = new CompatConfig();
+    private static final String TAG = "CompatConfig";
+    private static final String CONFIG_FILE_SUFFIX = "platform_compat_config.xml";
+
+    private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib(
+            Environment.buildPath(
+                    Environment.getRootDirectory(), "etc", "sysconfig"));
 
     @GuardedBy("mChanges")
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
@@ -128,20 +147,24 @@
      * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
      *
      * @param changeId The ID of the change to be overridden. Note, this call will succeed even if
-     *                 this change is not known; it will only have any affect if any code in the
+     *                 this change is not known; it will only have any effect if any code in the
      *                 platform is gated on the ID given.
      * @param packageName The app package name to override the change for.
      * @param enabled If the change should be enabled or disabled.
+     * @return {@code true} if the change existed before adding the override.
      */
-    public void addOverride(long changeId, String packageName, boolean enabled) {
+    public boolean addOverride(long changeId, String packageName, boolean enabled) {
+        boolean alreadyKnown = true;
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
             if (c == null) {
+                alreadyKnown = false;
                 c = new CompatChange(changeId);
                 addChange(c);
             }
             c.addPackageOverride(packageName, enabled);
         }
+        return alreadyKnown;
     }
 
     /**
@@ -151,14 +174,61 @@
      *
      * @param changeId The ID of the change that was overridden.
      * @param packageName The app package name that was overridden.
+     * @return {@code true} if an override existed;
      */
-    public void removeOverride(long changeId, String packageName) {
+    public boolean removeOverride(long changeId, String packageName) {
+        boolean overrideExists = false;
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
             if (c != null) {
+                overrideExists = true;
                 c.removePackageOverride(packageName);
             }
         }
+        return overrideExists;
+    }
+
+    /**
+    * Dumps the current list of compatibility config information.
+    *
+    * @param pw The {@link PrintWriter} instance to which the information will be dumped.
+    */
+    public void dumpConfig(PrintWriter pw) {
+        synchronized (mChanges) {
+            if (mChanges.size() == 0) {
+                pw.println("No compat overrides.");
+                return;
+            }
+            for (int i = 0; i < mChanges.size(); ++i) {
+                CompatChange c = mChanges.valueAt(i);
+                pw.println(c.toString());
+            }
+        }
+    }
+
+    CompatConfig initConfigFromLib(File libraryDir) {
+        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
+            Slog.e(TAG, "No directory " + libraryDir + ", skipping");
+            return this;
+        }
+        for (File f : libraryDir.listFiles()) {
+            //TODO(b/138222363): Handle duplicate ids across config files.
+            if (f.getPath().endsWith(CONFIG_FILE_SUFFIX)) {
+                readConfig(f);
+            }
+        }
+        return this;
+    }
+
+    private void readConfig(File configFile) {
+        try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+            for (Change change : XmlParser.read(in).getCompatChange()) {
+                Slog.w(TAG, "Adding: " + change.toString());
+                addChange(new CompatChange(change));
+            }
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e);
+        }
     }
 
 }
diff --git a/services/core/java/com/android/server/compat/IPlatformCompat.aidl b/services/core/java/com/android/server/compat/IPlatformCompat.aidl
new file mode 100644
index 0000000..8ab08f9
--- /dev/null
+++ b/services/core/java/com/android/server/compat/IPlatformCompat.aidl
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.compat;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * System private API for talking with the PlatformCompat service.
+ * {@hide}
+ */
+interface IPlatformCompat
+{
+
+    /**
+     * Reports that a compatibility change is affecting an app process now.
+     *
+     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
+     * you do not need to call this API directly. The change will be reported for you in the case
+     * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}.
+     *
+     * @param changeId The ID of the compatibility change taking effect.
+     * @param appInfo Representing the affected app.
+     */
+    void reportChange(long changeId, in ApplicationInfo appInfo);
+
+    /**
+     * Query if a given compatibility change is enabled for an app process. This method should
+     * be called when implementing functionality on behalf of the affected app.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method returns
+     * {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * <p>When this method returns {@code true}, it will also report the change as
+     * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method
+     * directly.
+     *
+     * @param changeId The ID of the compatibility change in question.
+     * @param appInfo Representing the app in question.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 456d15e..3eea194 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,52 +16,46 @@
 
 package com.android.server.compat;
 
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.util.Slog;
 
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * System server internal API for gating and reporting compatibility changes.
  */
-public class PlatformCompat {
+public class PlatformCompat extends IPlatformCompat.Stub {
 
     private static final String TAG = "Compatibility";
 
-    /**
-     * Reports that a compatibility change is affecting an app process now.
-     *
-     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
-     * you do not need to call this API directly. The change will be reported for you in the case
-     * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}.
-     *
-     * @param changeId The ID of the compatibility change taking effect.
-     * @param appInfo Representing the affected app.
-     */
-    public static void reportChange(long changeId, ApplicationInfo appInfo) {
+    private final Context mContext;
+
+    public PlatformCompat(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void reportChange(long changeId, ApplicationInfo appInfo) {
         Slog.d(TAG, "Compat change reported: " + changeId + "; UID " + appInfo.uid);
         // TODO log via StatsLog
     }
 
-    /**
-     * Query if a given compatibility change is enabled for an app process. This method should
-     * be called when implementing functionality on behalf of the affected app.
-     *
-     * <p>If this method returns {@code true}, the calling code should implement the compatibility
-     * change, resulting in differing behaviour compared to earlier releases. If this method returns
-     * {@code false}, the calling code should behave as it did in earlier releases.
-     *
-     * <p>When this method returns {@code true}, it will also report the change as
-     * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method
-     * directly.
-     *
-     * @param changeId The ID of the compatibility change in question.
-     * @param appInfo Representing the app in question.
-     * @return {@code true} if the change is enabled for the current app.
-     */
-    public static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
+    @Override
+    public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
         if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
             reportChange(changeId, appInfo);
             return true;
         }
         return false;
     }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
+        CompatConfig.get().dumpConfig(pw);
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 5b04379..96b7cb3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.IDnsResolver;
 import android.net.INetd;
@@ -116,7 +117,7 @@
 // not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
 public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
 
-    public NetworkInfo networkInfo;
+    @NonNull public NetworkInfo networkInfo;
     // This Network object should always be used if possible, so as to encourage reuse of the
     // enclosed socket factory and connection pool.  Avoid creating other Network objects.
     // This Network object is always valid.
@@ -579,10 +580,12 @@
         }
 
         if (newExpiry > 0) {
-            mLingerMessage = mConnService.makeWakeupMessage(
+            mLingerMessage = new WakeupMessage(
                     mContext, mHandler,
-                    "NETWORK_LINGER_COMPLETE." + network.netId,
-                    EVENT_NETWORK_LINGER_COMPLETE, this);
+                    "NETWORK_LINGER_COMPLETE." + network.netId /* cmdName */,
+                    EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
+                    0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
+                    this /* obj (NetworkAgentInfo) */);
             mLingerMessage.schedule(newExpiry);
         }
 
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 4957eed..73d160d 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -772,7 +772,6 @@
                     case WifiManager.WIFI_AP_STATE_FAILED:
                     default:
                         disableWifiIpServingLocked(ifname, curState);
-                        mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
                         break;
                 }
             }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index adc1cd7..8148216 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -290,8 +290,7 @@
             new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP),
             new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN),
             new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL),
-            // No Android keycode defined for CEC_KEYCODE_SOUND_SELECT
-            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SOUND_SELECT),
+            new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK, CEC_KEYCODE_SOUND_SELECT),
             new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT),
             new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION),
             // No Android keycode defined for CEC_KEYCODE_HELP
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 4f8b1dc..dad435b 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -492,6 +492,9 @@
             if (jobStatus.hasBatteryNotLowConstraint()) {
                 out.attribute(null, "battery-not-low", Boolean.toString(true));
             }
+            if (jobStatus.hasStorageNotLowConstraint()) {
+                out.attribute(null, "storage-not-low", Boolean.toString(true));
+            }
             out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS);
         }
 
@@ -852,6 +855,15 @@
             jobBuilder.setExtras(extras);
             parser.nextTag(); // Consume </extras>
 
+            final JobInfo builtJob;
+            try {
+                builtJob = jobBuilder.build();
+            } catch (Exception e) {
+                Slog.w(TAG, "Unable to build job from XML, ignoring: "
+                        + jobBuilder.summarize());
+                return null;
+            }
+
             // Migrate sync jobs forward from earlier, incomplete representation
             if ("android".equals(sourcePackageName)
                     && extras != null
@@ -935,6 +947,14 @@
             if (val != null) {
                 jobBuilder.setRequiresCharging(true);
             }
+            val = parser.getAttributeValue(null, "battery-not-low");
+            if (val != null) {
+                jobBuilder.setRequiresBatteryNotLow(true);
+            }
+            val = parser.getAttributeValue(null, "storage-not-low");
+            if (val != null) {
+                jobBuilder.setRequiresStorageNotLow(true);
+            }
         }
 
         /**
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index 4bc9373..b460cb5 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -2,5 +2,6 @@
 hdmoon@google.com
 insun@google.com
 jaewan@google.com
+klhyun@google.com
 lajos@google.com
 sungsoo@google.com
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index c98a79a..714bbb97 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -297,19 +297,5 @@
             }
             mDs.asBinder().unlinkToDeath(this, 0);
         }
-
-        // Old methods; unused in the API flow.
-        @Override
-        public void onProgressUpdated(int progress) throws RemoteException {
-        }
-
-        @Override
-        public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
-        }
-
-        @Override
-        public void onSectionComplete(String title, int status, int size, int durationMs)
-                throws RemoteException {
-        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 2b33ace..c712431 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -281,20 +281,7 @@
         mAbortIdleOptimization.set(false);
 
         long lowStorageThreshold = getLowStorageThreshold(context);
-        // Optimize primary apks.
-        int result = optimizePackages(pm, pkgs, lowStorageThreshold,
-            /*isForPrimaryDex=*/ true);
-        if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
-            return result;
-        }
-        if (supportSecondaryDex()) {
-            result = reconcileSecondaryDexFiles(pm.getDexManager());
-            if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
-                return result;
-            }
-            result = optimizePackages(pm, pkgs, lowStorageThreshold,
-                /*isForPrimaryDex=*/ false);
-        }
+        int result = idleOptimizePackages(pm, pkgs, lowStorageThreshold);
         return result;
     }
 
@@ -342,46 +329,88 @@
         return 0;
     }
 
-    private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
-            long lowStorageThreshold, boolean isForPrimaryDex) {
+    private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+            long lowStorageThreshold) {
         ArraySet<String> updatedPackages = new ArraySet<>();
-        Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
-        boolean hadSomeLowSpaceFailure = false;
-        Log.d(TAG, "Unsused Packages " +  String.join(",", unusedPackages));
-        // Only downgrade apps when space is low on device.
-        // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
-        // up disk before user hits the actual lowStorageThreshold.
-        final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
-                lowStorageThreshold;
-        boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
-        Log.d(TAG, "Should Downgrade " + shouldDowngrade);
-        boolean dex_opt_performed = false;
-        for (String pkg : pkgs) {
-            int abort_code = abortIdleOptimizations(lowStorageThreshold);
-            if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
-                return abort_code;
-            }
-            // Downgrade unused packages.
-            if (unusedPackages.contains(pkg) && shouldDowngrade) {
-                dex_opt_performed = downgradePackage(pm, pkg, isForPrimaryDex);
-            } else {
-                if (abort_code == OPTIMIZE_ABORT_NO_SPACE_LEFT) {
-                    // can't dexopt because of low space.
-                    hadSomeLowSpaceFailure = true;
-                    continue;
+
+        try {
+            final boolean supportSecondaryDex = supportSecondaryDex();
+
+            if (supportSecondaryDex) {
+                int result = reconcileSecondaryDexFiles(pm.getDexManager());
+                if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
+                    return result;
                 }
-                dex_opt_performed = optimizePackage(pm, pkg, isForPrimaryDex);
             }
-            if (dex_opt_performed) {
+
+            // Only downgrade apps when space is low on device.
+            // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
+            // up disk before user hits the actual lowStorageThreshold.
+            final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE
+                    * lowStorageThreshold;
+            boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
+            Log.d(TAG, "Should Downgrade " + shouldDowngrade);
+            if (shouldDowngrade) {
+                Set<String> unusedPackages =
+                        pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
+                Log.d(TAG, "Unsused Packages " +  String.join(",", unusedPackages));
+
+                if (!unusedPackages.isEmpty()) {
+                    for (String pkg : unusedPackages) {
+                        int abortCode = abortIdleOptimizations(/*lowStorageThreshold*/ -1);
+                        if (abortCode != OPTIMIZE_CONTINUE) {
+                            // Should be aborted by the scheduler.
+                            return abortCode;
+                        }
+                        if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) {
+                            updatedPackages.add(pkg);
+                        }
+                        if (supportSecondaryDex) {
+                            downgradePackage(pm, pkg, /*isForPrimaryDex*/ false);
+                        }
+                    }
+
+                    pkgs = new ArraySet<>(pkgs);
+                    pkgs.removeAll(unusedPackages);
+                }
+            }
+
+            int primaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
+                    /*isForPrimaryDex*/ true, updatedPackages);
+            if (primaryResult != OPTIMIZE_PROCESSED) {
+                return primaryResult;
+            }
+
+            if (!supportSecondaryDex) {
+                return OPTIMIZE_PROCESSED;
+            }
+
+            int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
+                    /*isForPrimaryDex*/ false, updatedPackages);
+            return secondaryResult;
+        } finally {
+            // Always let the pinner service know about changes.
+            notifyPinService(updatedPackages);
+        }
+    }
+
+    private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+            long lowStorageThreshold, boolean isForPrimaryDex, ArraySet<String> updatedPackages) {
+        for (String pkg : pkgs) {
+            int abortCode = abortIdleOptimizations(lowStorageThreshold);
+            if (abortCode != OPTIMIZE_CONTINUE) {
+                // Either aborted by the scheduler or no space left.
+                return abortCode;
+            }
+
+            boolean dexOptPerformed = optimizePackage(pm, pkg, isForPrimaryDex);
+            if (dexOptPerformed) {
                 updatedPackages.add(pkg);
             }
         }
-
-        notifyPinService(updatedPackages);
-        return hadSomeLowSpaceFailure ? OPTIMIZE_ABORT_NO_SPACE_LEFT : OPTIMIZE_PROCESSED;
+        return OPTIMIZE_PROCESSED;
     }
 
-
     /**
      * Try to downgrade the package to a smaller compilation filter.
      * eg. if the package is in speed-profile the package will be downgraded to verify.
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index f08e585..6ea274d 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -403,6 +403,10 @@
                     || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
                 return;
             }
+            ITvInputHardwareCallback callback = connection.getCallbackLocked();
+            if (callback != null) {
+                callback.asBinder().unlinkToDeath(connection, 0);
+            }
             connection.resetLocked(null, null, null, null, null);
         }
     }
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index 98e4343..8b2cbbd 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -4,3 +4,11 @@
     api_dir: "schema",
     package_name: "com.android.server.pm.permission.configfile",
 }
+
+
+xsd_config {
+    name: "platform-compat-config",
+    srcs: ["platform-compat-config.xsd"],
+    api_dir: "platform-compat-schema",
+    package_name: "com.android.server.compat.config",
+}
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
new file mode 100644
index 0000000..ee39e50
--- /dev/null
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<!-- This defines the format of the XML file generated by
+  ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from
+  ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java.
+-->
+<xs:schema version="2.0" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+    <xs:complexType name="change">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute type="xs:long" name="id" use="required"/>
+                <xs:attribute type="xs:string" name="name" use="required"/>
+                <xs:attribute type="xs:boolean" name="disabled"/>
+                <xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:element name="config">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="compat-change" type="change" maxOccurs="unbounded"
+                            minOccurs="0"/>
+            </xs:sequence>
+        </xs:complexType>
+        <xs:unique name="UniqueId">
+            <xs:selector xpath="compat-change" />
+            <xs:field xpath="@id" />
+        </xs:unique>
+    </xs:element>
+</xs:schema>
+
+
+
+
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
new file mode 100644
index 0000000..8456785
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -0,0 +1,31 @@
+// Signature format: 2.0
+package com.android.server.compat.config {
+
+  public class Change {
+    ctor public Change();
+    method public boolean getDisabled();
+    method public int getEnableAfterTargetSdk();
+    method public long getId();
+    method public String getName();
+    method public String getValue();
+    method public void setDisabled(boolean);
+    method public void setEnableAfterTargetSdk(int);
+    method public void setId(long);
+    method public void setName(String);
+    method public void setValue(String);
+  }
+
+  public class Config {
+    ctor public Config();
+    method public java.util.List<com.android.server.compat.config.Change> getCompatChange();
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static com.android.server.compat.config.Config read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat-schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat-schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat-schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dab1603..10db049 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -79,6 +79,7 @@
 import com.android.server.broadcastradio.BroadcastRadioService;
 import com.android.server.camera.CameraServiceProxy;
 import com.android.server.clipboard.ClipboardService;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.coverage.CoverageService;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -975,6 +976,11 @@
             traceBeginAndSlog("PinnerService");
             mSystemServiceManager.startService(PinnerService.class);
             traceEnd();
+
+            traceBeginAndSlog("PlatformCompat");
+            ServiceManager.addService("platform_compat", new PlatformCompat(context));
+            traceEnd();
+
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
diff --git a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
index bb56876..7c413779 100644
--- a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
+++ b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
@@ -21,13 +21,11 @@
  * @hide
  */
 public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
-    // TODO: add @Override here once the API is versioned
-
     /**
      * Get the version of the aidl interface implemented by the callbacks.
      */
+    @Override
     public int getInterfaceVersion() {
-        // TODO: return IDhcpServerCallbacks.VERSION;
-        return 0;
+        return IDhcpServerCallbacks.VERSION;
     }
 }
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 153820d..41f46f5 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -38,6 +38,7 @@
         "ub-uiautomator",
         "platformprotosnano",
         "servicestests-utils",
+        "xml-writer-device-lib",
     ],
 
     aidl: {
@@ -78,6 +79,8 @@
     optimize: {
         enabled: false,
     },
+
+    data: [":JobTestApp"],
 }
 
 java_library {
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index e6c484a..f3c5e99 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -22,9 +22,17 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compat.annotation.Change;
+import com.android.compat.annotation.XmlWriter;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.UUID;
+
 @RunWith(AndroidJUnit4.class)
 public class CompatConfigTest {
 
@@ -35,6 +43,27 @@
         return ai;
     }
 
+    private File createTempDir() {
+        String base = System.getProperty("java.io.tmpdir");
+        File dir = new File(base, UUID.randomUUID().toString());
+        assertThat(dir.mkdirs()).isTrue();
+        return dir;
+    }
+
+    private void writeChangesToFile(Change[] changes, File f) {
+        XmlWriter writer = new XmlWriter();
+        for (Change change: changes) {
+            writer.addChange(change);
+        }
+        try {
+            f.createNewFile();
+            writer.write(new FileOutputStream(f));
+        } catch (IOException e) {
+            throw new RuntimeException(
+                    "Encountered an error while writing compat config file", e);
+        }
+    }
+
     @Test
     public void testUnknownChangeEnabled() {
         CompatConfig pc = new CompatConfig();
@@ -142,4 +171,73 @@
         CompatConfig pc = new CompatConfig();
         assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
     }
+
+    @Test
+    public void testSystemAppDisabledChangeEnabled() {
+        CompatConfig pc = new CompatConfig();
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true)); // disabled
+        ApplicationInfo sysApp = makeAppInfo("system.app", 1);
+        sysApp.flags |= ApplicationInfo.FLAG_SYSTEM;
+        assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue();
+    }
+
+    @Test
+    public void testSystemAppOverrideIgnored() {
+        CompatConfig pc = new CompatConfig();
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false));
+        pc.addOverride(1234L, "system.app", false);
+        ApplicationInfo sysApp = makeAppInfo("system.app", 1);
+        sysApp.flags |= ApplicationInfo.FLAG_SYSTEM;
+        assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue();
+    }
+
+    @Test
+    public void testSystemAppTargetSdkIgnored() {
+        CompatConfig pc = new CompatConfig();
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false));
+        ApplicationInfo sysApp = makeAppInfo("system.app", 1);
+        sysApp.flags |= ApplicationInfo.FLAG_SYSTEM;
+        assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue();
+    }
+
+    @Test
+    public void testReadConfig() {
+        Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2), new Change(1235L,
+                "MY_CHANGE2", true, null), new Change(1236L, "MY_CHANGE3", false, null)};
+
+        File dir = createTempDir();
+        writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml"));
+
+        CompatConfig pc = new CompatConfig();
+        pc.initConfigFromLib(dir);
+
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+        assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
+        assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+    }
+
+    @Test
+    public void testReadConfigMultipleFiles() {
+        Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2)};
+        Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null), new Change(1236L,
+                "MY_CHANGE3", false, null)};
+
+        File dir = createTempDir();
+        writeChangesToFile(changes1,
+                new File(dir.getPath() + "/libcore_platform_compat_config.xml"));
+        writeChangesToFile(changes2,
+                new File(dir.getPath() + "/frameworks_platform_compat_config.xml"));
+
+
+        CompatConfig pc = new CompatConfig();
+        pc.initConfigFromLib(dir);
+
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+        assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
+        assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+    }
 }
+
+
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 543f51cba..6fa5cd29 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -382,6 +382,82 @@
                 .build());
     }
 
+    @Test
+    public void testPersistedIdleConstraint() throws Exception {
+        JobInfo.Builder b = new Builder(8, mComponent)
+                .setRequiresDeviceIdle(true)
+                .setPersisted(true);
+        JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+
+        mTaskStoreUnderTest.add(taskStatus);
+        waitForPendingIo();
+
+        final JobSet jobStatusSet = new JobSet();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+        assertEquals("Idle constraint not persisted correctly.",
+                loaded.getJob().isRequireDeviceIdle(),
+                taskStatus.getJob().isRequireDeviceIdle());
+    }
+
+    @Test
+    public void testPersistedChargingConstraint() throws Exception {
+        JobInfo.Builder b = new Builder(8, mComponent)
+                .setRequiresCharging(true)
+                .setPersisted(true);
+        JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+
+        mTaskStoreUnderTest.add(taskStatus);
+        waitForPendingIo();
+
+        final JobSet jobStatusSet = new JobSet();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+        assertEquals("Charging constraint not persisted correctly.",
+                loaded.getJob().isRequireCharging(),
+                taskStatus.getJob().isRequireCharging());
+    }
+
+    @Test
+    public void testPersistedStorageNotLowConstraint() throws Exception {
+        JobInfo.Builder b = new Builder(8, mComponent)
+                .setRequiresStorageNotLow(true)
+                .setPersisted(true);
+        JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+
+        mTaskStoreUnderTest.add(taskStatus);
+        waitForPendingIo();
+
+        final JobSet jobStatusSet = new JobSet();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+        assertEquals("Storage-not-low constraint not persisted correctly.",
+                loaded.getJob().isRequireStorageNotLow(),
+                taskStatus.getJob().isRequireStorageNotLow());
+    }
+
+    @Test
+    public void testPersistedBatteryNotLowConstraint() throws Exception {
+        JobInfo.Builder b = new Builder(8, mComponent)
+                .setRequiresBatteryNotLow(true)
+                .setPersisted(true);
+        JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+
+        mTaskStoreUnderTest.add(taskStatus);
+        waitForPendingIo();
+
+        final JobSet jobStatusSet = new JobSet();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+        JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+        assertEquals("Battery-not-low constraint not persisted correctly.",
+                loaded.getJob().isRequireBatteryNotLow(),
+                taskStatus.getJob().isRequireBatteryNotLow());
+    }
+
     /**
      * Helper function to kick a {@link JobInfo} through a persistence cycle and
      * assert that it's unchanged.
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
index ae1eca7..b29e187 100644
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.bp
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
@@ -17,8 +17,6 @@
 
     sdk_version: "current",
 
-    test_suites: ["device-tests"],
-
     srcs: ["**/*.java"],
 
     dex_preopt: {
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index 7a1678a..a4906d7 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -23,4 +23,5 @@
         "src/FrameLayoutInflationActivity.java",
         "src/TextViewInflationActivity.java",
     ],
+    platform_apis: true,
 }
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 92ea872..4f6524e 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -77,7 +77,6 @@
     name: "view-compiler-tests",
     defaults: ["viewcompiler_defaults"],
     srcs: [
-        "dex_builder_test.cc",
         "layout_validation_test.cc",
         "util_test.cc",
     ],
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 6047e8c..499c42e 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -102,6 +102,18 @@
     case Instruction::Op::kCheckCast:
       out << "kCheckCast";
       return out;
+    case Instruction::Op::kGetStaticField:
+      out << "kGetStaticField";
+      return out;
+    case Instruction::Op::kSetStaticField:
+      out << "kSetStaticField";
+      return out;
+    case Instruction::Op::kGetInstanceField:
+      out << "kGetInstanceField";
+      return out;
+    case Instruction::Op::kSetInstanceField:
+      out << "kSetInstanceField";
+      return out;
   }
 }
 
@@ -229,6 +241,23 @@
   return type;
 }
 
+ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::string& name,
+                                         TypeDescriptor type) {
+  const auto key = std::make_tuple(parent, name);
+  if (field_decls_by_key_.find(key) != field_decls_by_key_.end()) {
+    return field_decls_by_key_[key];
+  }
+
+  ir::FieldDecl* field = Alloc<ir::FieldDecl>();
+  field->parent = GetOrAddType(parent);
+  field->name = GetOrAddString(name);
+  field->type = GetOrAddType(type);
+  field->orig_index = dex_file_->fields_indexes.AllocateIndex();
+  dex_file_->fields_map[field->orig_index] = field;
+  field_decls_by_key_[key] = field;
+  return field;
+}
+
 ir::Proto* Prototype::Encode(DexBuilder* dex) const {
   auto* proto = dex->Alloc<ir::Proto>();
   proto->shorty = dex->GetOrAddString(Shorty());
@@ -360,6 +389,11 @@
       return EncodeNew(instruction);
     case Instruction::Op::kCheckCast:
       return EncodeCast(instruction);
+    case Instruction::Op::kGetStaticField:
+    case Instruction::Op::kSetStaticField:
+    case Instruction::Op::kGetInstanceField:
+    case Instruction::Op::kSetInstanceField:
+      return EncodeFieldOp(instruction);
   }
 }
 
@@ -428,7 +462,7 @@
     // first move all the arguments into contiguous temporary registers.
     std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
 
-    const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id());
+    const auto& prototype = dex_->GetPrototypeByMethodId(instruction.index_argument());
     CHECK(prototype.has_value());
 
     for (size_t i = 0; i < instruction.args().size(); ++i) {
@@ -452,12 +486,12 @@
 
     Encode3rc(InvokeToInvokeRange(opcode),
               instruction.args().size(),
-              instruction.method_id(),
+              instruction.index_argument(),
               RegisterValue(scratch[0]));
   } else {
     Encode35c(opcode,
               instruction.args().size(),
-              instruction.method_id(),
+              instruction.index_argument(),
               arguments[0],
               arguments[1],
               arguments[2],
@@ -514,6 +548,54 @@
   Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
 }
 
+void MethodBuilder::EncodeFieldOp(const Instruction& instruction) {
+  const auto& args = instruction.args();
+  switch (instruction.opcode()) {
+    case Instruction::Op::kGetStaticField: {
+      CHECK(instruction.dest().has_value());
+      CHECK(instruction.dest()->is_variable());
+      CHECK_EQ(0, instruction.args().size());
+
+      Encode21c(::art::Instruction::SGET,
+                RegisterValue(*instruction.dest()),
+                instruction.index_argument());
+      break;
+    }
+    case Instruction::Op::kSetStaticField: {
+      CHECK(!instruction.dest().has_value());
+      CHECK_EQ(1, args.size());
+      CHECK(args[0].is_variable());
+
+      Encode21c(::art::Instruction::SPUT, RegisterValue(args[0]), instruction.index_argument());
+      break;
+    }
+    case Instruction::Op::kGetInstanceField: {
+      CHECK(instruction.dest().has_value());
+      CHECK(instruction.dest()->is_variable());
+      CHECK_EQ(1, instruction.args().size());
+
+      Encode22c(::art::Instruction::IGET,
+                RegisterValue(*instruction.dest()),
+                RegisterValue(args[0]),
+                instruction.index_argument());
+      break;
+    }
+    case Instruction::Op::kSetInstanceField: {
+      CHECK(!instruction.dest().has_value());
+      CHECK_EQ(2, args.size());
+      CHECK(args[0].is_variable());
+      CHECK(args[1].is_variable());
+
+      Encode22c(::art::Instruction::IPUT,
+                RegisterValue(args[1]),
+                RegisterValue(args[0]),
+                instruction.index_argument());
+      break;
+    }
+    default: { LOG(FATAL) << "Unsupported field operation"; }
+  }
+}
+
 size_t MethodBuilder::RegisterValue(const Value& value) const {
   if (value.is_register()) {
     return value.value();
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 541d800..292d659 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -153,6 +153,8 @@
     kBranchEqz,
     kBranchNEqz,
     kCheckCast,
+    kGetInstanceField,
+    kGetStaticField,
     kInvokeDirect,
     kInvokeInterface,
     kInvokeStatic,
@@ -162,6 +164,8 @@
     kNew,
     kReturn,
     kReturnObject,
+    kSetInstanceField,
+    kSetStaticField
   };
 
   ////////////////////////
@@ -170,12 +174,12 @@
 
   // For instructions with no return value and no arguments.
   static inline Instruction OpNoArgs(Op opcode) {
-    return Instruction{opcode, /*method_id*/ 0, /*dest*/ {}};
+    return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}};
   }
   // For most instructions, which take some number of arguments and have an optional return value.
   template <typename... T>
   static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
-    return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
+    return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
   }
 
   // A cast instruction. Basically, `(type)val`
@@ -186,49 +190,71 @@
 
   // For method calls.
   template <typename... T>
-  static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest,
                                           Value this_arg, T... args) {
     return Instruction{
-        Op::kInvokeVirtual, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+        Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
   }
   // Returns an object
   template <typename... T>
-  static inline Instruction InvokeVirtualObject(size_t method_id, std::optional<const Value> dest,
-                                                Value this_arg, T... args) {
+  static inline Instruction InvokeVirtualObject(size_t index_argument,
+                                                std::optional<const Value> dest, Value this_arg,
+                                                T... args) {
     return Instruction{
-        Op::kInvokeVirtual, method_id, /*result_is_object=*/true, dest, this_arg, args...};
+        Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
   }
   // For direct calls (basically, constructors).
   template <typename... T>
-  static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
                                          Value this_arg, T... args) {
     return Instruction{
-        Op::kInvokeDirect, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+        Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
   }
   // Returns an object
   template <typename... T>
-  static inline Instruction InvokeDirectObject(size_t method_id, std::optional<const Value> dest,
-                                               Value this_arg, T... args) {
-    return Instruction{
-        Op::kInvokeDirect, method_id, /*result_is_object=*/true, dest, this_arg, args...};
-  }
-  // For static calls.
-  template <typename... T>
-  static inline Instruction InvokeStatic(size_t method_id, std::optional<const Value> dest,
-                                         T... args) {
-    return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/false, dest, args...};
-  }
-  // Returns an object
-  template <typename... T>
-  static inline Instruction InvokeStaticObject(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeDirectObject(size_t index_argument,
+                                               std::optional<const Value> dest, Value this_arg,
                                                T... args) {
-    return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/true, dest, args...};
+    return Instruction{
+        Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
   }
   // For static calls.
   template <typename... T>
-  static inline Instruction InvokeInterface(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
+                                         T... args) {
+    return Instruction{
+        Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...};
+  }
+  // Returns an object
+  template <typename... T>
+  static inline Instruction InvokeStaticObject(size_t index_argument,
+                                               std::optional<const Value> dest, T... args) {
+    return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
+  }
+  // For static calls.
+  template <typename... T>
+  static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
                                             T... args) {
-    return Instruction{Op::kInvokeInterface, method_id, /*result_is_object=*/false, dest, args...};
+    return Instruction{
+        Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
+  }
+
+  static inline Instruction GetStaticField(size_t field_id, Value dest) {
+    return Instruction{Op::kGetStaticField, field_id, dest};
+  }
+
+  static inline Instruction SetStaticField(size_t field_id, Value value) {
+    return Instruction{
+        Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value};
+  }
+
+  static inline Instruction GetField(size_t field_id, Value dest, Value object) {
+    return Instruction{Op::kGetInstanceField, field_id, /*result_is_object=*/false, dest, object};
+  }
+
+  static inline Instruction SetField(size_t field_id, Value object, Value value) {
+    return Instruction{
+        Op::kSetInstanceField, field_id, /*result_is_object=*/false, /*dest=*/{}, object, value};
   }
 
   ///////////////
@@ -236,27 +262,31 @@
   ///////////////
 
   Op opcode() const { return opcode_; }
-  size_t method_id() const { return method_id_; }
+  size_t index_argument() const { return index_argument_; }
   bool result_is_object() const { return result_is_object_; }
   const std::optional<const Value>& dest() const { return dest_; }
   const std::vector<const Value>& args() const { return args_; }
 
  private:
-  inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest)
-      : opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {}
+  inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
+      : opcode_{opcode},
+        index_argument_{index_argument},
+        result_is_object_{false},
+        dest_{dest},
+        args_{} {}
 
   template <typename... T>
-  inline constexpr Instruction(Op opcode, size_t method_id, bool result_is_object,
+  inline Instruction(Op opcode, size_t index_argument, bool result_is_object,
                                std::optional<const Value> dest, T... args)
       : opcode_{opcode},
-        method_id_{method_id},
+        index_argument_{index_argument},
         result_is_object_{result_is_object},
         dest_{dest},
         args_{args...} {}
 
   const Op opcode_;
   // The index of the method to invoke, for kInvokeVirtual and similar opcodes.
-  const size_t method_id_{0};
+  const size_t index_argument_{0};
   const bool result_is_object_;
   const std::optional<const Value> dest_;
   const std::vector<const Value> args_;
@@ -319,6 +349,7 @@
   void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
   void EncodeNew(const Instruction& instruction);
   void EncodeCast(const Instruction& instruction);
+  void EncodeFieldOp(const Instruction& instruction);
 
   // Low-level instruction format encoding. See
   // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
@@ -351,6 +382,14 @@
     buffer_.push_back(b);
   }
 
+  inline void Encode22c(art::Instruction::Code opcode, uint8_t a, uint8_t b, uint16_t c) {
+    // b|a|op|bbbb
+    CHECK(IsShortRegister(a));
+    CHECK(IsShortRegister(b));
+    buffer_.push_back((b << 12) | (a << 8) | opcode);
+    buffer_.push_back(c);
+  }
+
   inline void Encode32x(art::Instruction::Code opcode, uint16_t a, uint16_t b) {
     buffer_.push_back(opcode);
     buffer_.push_back(a);
@@ -481,6 +520,11 @@
   // See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
   // imported classes.
   ir::Type* GetOrAddType(const std::string& descriptor);
+  inline ir::Type* GetOrAddType(TypeDescriptor descriptor) {
+    return GetOrAddType(descriptor.descriptor());
+  }
+
+  ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type);
 
   // Returns the method id for the method, creating it if it has not been created yet.
   const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
@@ -526,6 +570,9 @@
 
   // Keep track of already-encoded protos.
   std::map<Prototype, ir::Proto*> proto_map_;
+
+  // Keep track of fields that have been declared
+  std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_;
 };
 
 template <typename... T>
diff --git a/startop/view_compiler/dex_builder_test.cc b/startop/view_compiler/dex_builder_test.cc
deleted file mode 100644
index 90c256f..0000000
--- a/startop/view_compiler/dex_builder_test.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#include "dex_builder.h"
-
-#include "dex/art_dex_file_loader.h"
-#include "dex/dex_file.h"
-#include "gtest/gtest.h"
-
-using namespace startop::dex;
-
-// Takes a DexBuilder, encodes it into an in-memory DEX file, verifies the resulting DEX file and
-// returns whether the verification was successful.
-bool EncodeAndVerify(DexBuilder* dex_file) {
-  slicer::MemView image{dex_file->CreateImage()};
-
-  art::ArtDexFileLoader loader;
-  std::string error_msg;
-  std::unique_ptr<const art::DexFile> loaded_dex_file{loader.Open(image.ptr<const uint8_t>(),
-                                                                  image.size(),
-                                                                  /*location=*/"",
-                                                                  /*location_checksum=*/0,
-                                                                  /*oat_dex_file=*/nullptr,
-                                                                  /*verify=*/true,
-                                                                  /*verify_checksum=*/false,
-                                                                  &error_msg)};
-  return loaded_dex_file != nullptr;
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-//     public static void foo() {}
-// }
-TEST(DexBuilderTest, VerifyDexWithClassMethod) {
-  DexBuilder dex_file;
-
-  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
-  auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void()})};
-  method.BuildReturn();
-  method.Encode();
-
-  EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
-
-// Makes sure a bad DEX class fails to verify.
-TEST(DexBuilderTest, VerifyBadDexWithClassMethod) {
-  DexBuilder dex_file;
-
-  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
-  // This method has the error, because methods cannot take Void() as a parameter.
-  auto method{
-      cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void(), TypeDescriptor::Void()})};
-  method.BuildReturn();
-  method.Encode();
-
-  EXPECT_FALSE(EncodeAndVerify(&dex_file));
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-//     public static int foo() { return 5; }
-// }
-TEST(DexBuilderTest, VerifyDexReturn5) {
-  DexBuilder dex_file;
-
-  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
-  auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
-  auto r = method.MakeRegister();
-  method.BuildConst4(r, 5);
-  method.BuildReturn(r);
-  method.Encode();
-
-  EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-//     public static int foo(int x) { return x; }
-// }
-TEST(DexBuilderTest, VerifyDexReturnIntParam) {
-  DexBuilder dex_file;
-
-  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
-  auto method{
-      cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
-  method.BuildReturn(Value::Parameter(0));
-  method.Encode();
-
-  EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-//     public static int foo(String s) { return s.length(); }
-// }
-TEST(DexBuilderTest, VerifyDexCallStringLength) {
-  DexBuilder dex_file;
-
-  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
-  MethodBuilder method{cbuilder.CreateMethod(
-      "foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::FromClassname("java.lang.String")})};
-
-  Value result = method.MakeRegister();
-
-  MethodDeclData string_length =
-      dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
-                                  "length",
-                                  Prototype{TypeDescriptor::Int()});
-
-  method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
-  method.BuildReturn(result);
-
-  method.Encode();
-
-  EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
-
-// Write out and verify a DEX file that corresponds to:
-//
-// package dextest;
-// public class DexTest {
-//     public static int foo(String s) { return s.length(); }
-// }
-TEST(DexBuilderTest, VerifyDexCallManyRegisters) {
-  DexBuilder dex_file;
-
-  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
-
-  MethodBuilder method{cbuilder.CreateMethod(
-      "foo", Prototype{TypeDescriptor::Int()})};
-
-  Value result = method.MakeRegister();
-
-  // Make a bunch of registers
-  for (size_t i = 0; i < 25; ++i) {
-    method.MakeRegister();
-  }
-
-  // Now load a string literal into a register
-  Value string_val = method.MakeRegister();
-  method.BuildConstString(string_val, "foo");
-
-  MethodDeclData string_length =
-      dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
-                                  "length",
-                                  Prototype{TypeDescriptor::Int()});
-
-  method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, string_val));
-  method.BuildReturn(result);
-
-  method.Encode();
-
-  EXPECT_TRUE(EncodeAndVerify(&dex_file));
-}
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index ac60e96..9ad1ca1 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -39,6 +39,7 @@
     srcs: [
         "src/android/startop/test/DexBuilderTest.java",
         "src/android/startop/test/LayoutCompilerTest.java",
+        "src/android/startop/test/TestClass.java",
     ],
     sdk_version: "current",
     data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 42d4161..93496d0 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -28,10 +28,10 @@
 
 // Adding tests here requires changes in several other places. See README.md in
 // the view_compiler directory for more information.
-public class DexBuilderTest {
+public final class DexBuilderTest {
   static ClassLoader loadDexFile(String filename) throws Exception {
     return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
-        ClassLoader.getSystemClassLoader());
+        DexBuilderTest.class.getClassLoader());
   }
 
   public void hello() {}
@@ -171,4 +171,44 @@
     }
     Assert.assertTrue(castFailed);
   }
+
+  @Test
+  public void readStaticField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("readStaticField");
+    TestClass.staticInteger = 5;
+    Assert.assertEquals(5, method.invoke(null));
+  }
+
+  @Test
+  public void setStaticField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("setStaticField");
+    TestClass.staticInteger = 5;
+    method.invoke(null);
+    Assert.assertEquals(7, TestClass.staticInteger);
+  }
+
+  @Test
+  public void readInstanceField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("readInstanceField", TestClass.class);
+    TestClass obj = new TestClass();
+    obj.instanceField = 5;
+    Assert.assertEquals(5, method.invoke(null, obj));
+  }
+
+  @Test
+  public void setInstanceField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("setInstanceField", TestClass.class);
+    TestClass obj = new TestClass();
+    obj.instanceField = 5;
+    method.invoke(null, obj);
+    Assert.assertEquals(7, obj.instanceField);
+  }
 }
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
new file mode 100644
index 0000000..dd77923
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 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.startop.test;
+
+ /**
+ * A simple class to help test DexBuilder.
+ */
+public final class TestClass {
+    public static int staticInteger;
+
+    public int instanceField;
+}
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
index c68793d..8febfb7 100644
--- a/startop/view_compiler/dex_layout_compiler.cc
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -23,25 +23,6 @@
 
 using android::base::StringPrintf;
 
-void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
-  if (0 == name.compare(u"merge")) {
-    message_ = "Merge tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"include")) {
-    message_ = "Include tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"view")) {
-    message_ = "View tags are not supported";
-    can_compile_ = false;
-  }
-  if (0 == name.compare(u"fragment")) {
-    message_ = "Fragment tags are not supported";
-    can_compile_ = false;
-  }
-}
-
 DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
     : method_{method},
       context_{dex::Value::Parameter(0)},
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index f62ec5dd..6dedf24 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -282,6 +282,62 @@
     method.Encode();
   }(castObjectToString);
 
+  TypeDescriptor test_class = TypeDescriptor::FromClassname("android.startop.test.TestClass");
+
+  // Read a static field
+  // int readStaticField() { return TestClass.staticInteger; }
+  MethodBuilder readStaticField{
+      cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
+    Value result{method.MakeRegister()};
+    method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
+    method.BuildReturn(result, /*is_object=*/false);
+    method.Encode();
+  }(readStaticField);
+
+  // Set a static field
+  // void setStaticField() { TestClass.staticInteger = 7; }
+  MethodBuilder setStaticField{
+      cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
+    Value number{method.MakeRegister()};
+    method.BuildConst4(number, 7);
+    method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
+    method.BuildReturn();
+    method.Encode();
+  }(setStaticField);
+
+  // Read an instance field
+  // int readInstanceField(TestClass obj) { return obj.instanceField; }
+  MethodBuilder readInstanceField{
+      cbuilder.CreateMethod("readInstanceField", Prototype{TypeDescriptor::Int(), test_class})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
+    Value result{method.MakeRegister()};
+    method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
+    method.BuildReturn(result, /*is_object=*/false);
+    method.Encode();
+  }(readInstanceField);
+
+  // Set an instance field
+  // void setInstanceField(TestClass obj) { obj.instanceField = 7; }
+  MethodBuilder setInstanceField{
+      cbuilder.CreateMethod("setInstanceField", Prototype{TypeDescriptor::Void(), test_class})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
+    Value number{method.MakeRegister()};
+    method.BuildConst4(number, 7);
+    method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
+    method.BuildReturn();
+    method.Encode();
+  }(setInstanceField);
+
   slicer::MemView image{dex_file.CreateImage()};
   std::ofstream out_file(outdir + "/simple.dex");
   out_file.write(image.ptr<const char>(), image.size());
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 83aa521..42eaa8d 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2975,6 +2975,7 @@
              * The {@code content://} style URL for this table. Can be appended with a part ID to
              * address individual parts.
              */
+            @NonNull
             public static final Uri CONTENT_URI = Uri.withAppendedPath(Mms.CONTENT_URI, "part");
 
             /**
@@ -3158,6 +3159,8 @@
 
         /**
          * The {@code content://} style URL for locked messages in this table.
+         * <P>This {@link Uri} is used to check at most one locked message found in the union of MMS
+         * and SMS messages. Also this will return only _id column in response.</P>
          */
         public static final Uri CONTENT_LOCKED_URI = Uri.parse(
                 "content://mms-sms/locked");
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 31cd608..44c50f0 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -852,6 +852,19 @@
             "carrier_metered_roaming_apn_types_strings";
 
     /**
+     * APN types that are not allowed on cellular
+     * @hide
+     */
+    public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
+            "carrier_wwan_disallowed_apn_types_string_array";
+
+    /**
+     * APN types that are not allowed on IWLAN
+     * @hide
+     */
+    public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
+            "carrier_wlan_disallowed_apn_types_string_array";
+    /**
      * CDMA carrier ERI (Enhanced Roaming Indicator) file name
      * @hide
      */
@@ -2377,6 +2390,14 @@
     public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL =
             "check_pricing_with_carrier_data_roaming_bool";
 
+     /**
+      * Determines whether we should show a notification when the phone established a data
+      * connection in roaming network, to warn users about possible roaming charges.
+      * @hide
+      */
+    public static final String KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL =
+            "show_data_connected_roaming_notification";
+
     /**
      * A list of 4 LTE RSRP thresholds above which a signal level is considered POOR,
      * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
@@ -2804,6 +2825,23 @@
             "is_opportunistic_subscription_bool";
 
     /**
+     * Configs used by the IMS stack.
+     */
+    public static final class Ims {
+        /** Prefix of all Ims.KEY_* constants. */
+        public static final String KEY_PREFIX = "ims.";
+
+        //TODO: Add configs related to IMS.
+
+        private Ims() {}
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            return defaults;
+        }
+    }
+
+    /**
      * A list of 4 GSM RSSI thresholds above which a signal level is considered POOR,
      * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
      *
@@ -2817,6 +2855,15 @@
     public static final String KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY =
             "gsm_rssi_thresholds_int_array";
 
+    /**
+     * Determines whether Wireless Priority Service call is supported over IMS.
+     *
+     * See Wireless Priority Service from https://www.fcc.gov/general/wireless-priority-service-wps
+     * @hide
+     */
+    public static final String KEY_SUPPORT_WPS_OVER_IMS_BOOL =
+            "support_wps_over_ims_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -2951,6 +2998,10 @@
                 new String[]{"default", "mms", "dun", "supl"});
         sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{"default", "mms", "dun", "supl"});
+        sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
+                new String[]{""});
+        sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
+                new String[]{""});
         sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
                 new int[]{
                     4, /* IS95A */
@@ -3157,6 +3208,7 @@
         sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, "");
         sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false);
         sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
                 new int[] {
                         -128, /* SIGNAL_STRENGTH_POOR */
@@ -3217,6 +3269,8 @@
                         -97, /* SIGNAL_STRENGTH_GOOD */
                         -89,  /* SIGNAL_STRENGTH_GREAT */
                 });
+        sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+        sDefaults.putAll(Ims.getDefaults());
     }
 
     /**
@@ -3413,4 +3467,75 @@
         return ICarrierConfigLoader.Stub
                 .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE));
     }
+
+    /**
+     * Gets the configuration values for a component using its prefix.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * @param prefix prefix of the component.
+     * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+     *
+     * @see #getConfigForSubId
+     */
+    @Nullable
+    public PersistableBundle getConfigByComponentForSubId(String prefix, int subId) {
+        PersistableBundle configs = getConfigForSubId(subId);
+
+        if (configs == null) {
+            return null;
+        }
+
+        PersistableBundle ret = new PersistableBundle();
+        for (String configKey : configs.keySet()) {
+            if (configKey.startsWith(prefix)) {
+                addConfig(configKey, configs.get(configKey), ret);
+            }
+        }
+
+        return ret;
+    }
+
+    private void addConfig(String key, Object value, PersistableBundle configs) {
+        if (value instanceof String) {
+            configs.putString(key, (String) value);
+        }
+
+        if (value instanceof String[]) {
+            configs.putStringArray(key, (String[]) value);
+        }
+
+        if (value instanceof Integer) {
+            configs.putInt(key, (Integer) value);
+        }
+
+        if (value instanceof Long) {
+            configs.putLong(key, (Long) value);
+        }
+
+        if (value instanceof Double) {
+            configs.putDouble(key, (Double) value);
+        }
+
+        if (value instanceof Boolean) {
+            configs.putBoolean(key, (Boolean) value);
+        }
+
+        if (value instanceof int[]) {
+            configs.putIntArray(key, (int[]) value);
+        }
+
+        if (value instanceof double[]) {
+            configs.putDoubleArray(key, (double[]) value);
+        }
+
+        if (value instanceof boolean[]) {
+            configs.putBooleanArray(key, (boolean[]) value);
+        }
+
+        if (value instanceof long[]) {
+            configs.putLongArray(key, (long[]) value);
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 127eabd..31b3a05 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -143,7 +143,7 @@
     }
 
     /**
-     * Get the signal strength as dBm
+     * Get the signal strength as dBm.
      */
     @Override
     public int getDbm() {
@@ -163,18 +163,17 @@
     }
 
     /**
-     * Return the Received Signal Strength Indicator
+     * Return the Received Signal Strength Indicator.
      *
      * @return the RSSI in dBm (-113, -51) or
      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
-     * @hide
      */
     public int getRssi() {
         return mRssi;
     }
 
     /**
-     * Return the Bit Error Rate
+     * Return the Bit Error Rate.
      *
      * @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index b75e515..f03a9dc 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -22,9 +22,11 @@
 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
 
 import android.annotation.IntDef;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.location.CountryDetector;
 import android.net.Uri;
@@ -164,6 +166,33 @@
         return c == 'w'||c == 'W';
     }
 
+    private static int sMinMatch = 0;
+
+    private static int getMinMatch() {
+        if (sMinMatch == 0) {
+            sMinMatch = Resources.getSystem().getInteger(
+                    com.android.internal.R.integer.config_phonenumber_compare_min_match);
+        }
+        return sMinMatch;
+    }
+
+    /**
+     * A Test API to get current sMinMatch.
+     * @hide
+     */
+    @TestApi
+    public static int getMinMatchForTest() {
+        return getMinMatch();
+    }
+
+    /**
+     * A Test API to set sMinMatch.
+     * @hide
+     */
+    @TestApi
+    public static void setMinMatchForTest(int minMatch) {
+        sMinMatch = minMatch;
+    }
 
     /** Returns true if ch is not dialable or alpha char */
     private static boolean isSeparator(char ch) {
@@ -475,7 +504,7 @@
      * enough for caller ID purposes.
      *
      * - Compares from right to left
-     * - requires MIN_MATCH (7) characters to match
+     * - requires minimum characters to match
      * - handles common trunk prefixes and international prefixes
      *   (basically, everything except the Russian trunk prefix)
      *
@@ -491,6 +520,7 @@
         int matched;
         int numNonDialableCharsInA = 0;
         int numNonDialableCharsInB = 0;
+        int minMatch = getMinMatch();
 
         if (a == null || b == null) return a == b;
 
@@ -530,12 +560,12 @@
             }
         }
 
-        if (matched < MIN_MATCH) {
+        if (matched < minMatch) {
             int effectiveALen = a.length() - numNonDialableCharsInA;
             int effectiveBLen = b.length() - numNonDialableCharsInB;
 
 
-            // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH,
+            // if the number of dialable chars in a and b match, but the matched chars < minMatch,
             // treat them as equal (i.e. 404-04 and 40404)
             if (effectiveALen == effectiveBLen && effectiveALen == matched) {
                 return true;
@@ -545,7 +575,7 @@
         }
 
         // At least one string has matched completely;
-        if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
+        if (matched >= minMatch && (ia < 0 || ib < 0)) {
             return true;
         }
 
@@ -736,7 +766,7 @@
     }
 
     /**
-     * Returns the rightmost MIN_MATCH (5) characters in the network portion
+     * Returns the rightmost minimum matched characters in the network portion
      * in *reversed* order
      *
      * This can be used to do a database lookup against the column
@@ -747,7 +777,7 @@
     public static String
     toCallerIDMinMatch(String phoneNumber) {
         String np = extractNetworkPortionAlt(phoneNumber);
-        return internalGetStrippedReversed(np, MIN_MATCH);
+        return internalGetStrippedReversed(np, getMinMatch());
     }
 
     /**
@@ -1709,26 +1739,6 @@
         return normalizedDigits.toString();
     }
 
-    // Three and four digit phone numbers for either special services,
-    // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
-    // not match.
-    //
-    // This constant used to be 5, but SMS short codes has increased in length and
-    // can be easily 6 digits now days. Most countries have SMS short code length between
-    // 3 to 6 digits. The exceptions are
-    //
-    // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
-    //            followed by an additional four or six digits and two.
-    // Czechia: Codes are seven digits in length for MO and five (not billed) or
-    //            eight (billed) for MT direction
-    //
-    // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
-    //
-    // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
-    // to 7.
-    @UnsupportedAppUsage
-    static final int MIN_MATCH = 7;
-
     /**
      * Checks a given number against the list of
      * emergency numbers provided by the RIL and SIM card.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fe81215..d72ca6b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2327,6 +2327,10 @@
      * <p>
      * The ISO-3166 country code is provided in lowercase 2 character format.
      * <p>
+     * Note: In multi-sim, this returns a shared emergency network country iso from other
+     * subscription if the subscription used to create the TelephonyManager doesn't camp on
+     * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
+     * slot.
      * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
      * if on a CDMA network).
      * <p>
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index aaa98eb..733fb1e 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -22,6 +22,7 @@
 import android.hardware.radio.V1_4.EmergencyServiceCategory;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
 
@@ -301,6 +302,9 @@
      * The character in the number string is only the dial pad
      * character('0'-'9', '*', '+', or '#'). For example: 911.
      *
+     * If the number starts with carrier prefix, the carrier prefix is configured in
+     * {@link CarrierConfigManager#KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY}.
+     *
      * @return the dialing number.
      */
     public @NonNull String getNumber() {
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 8c686f7..8e1324b 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.pm.PackageManager;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
@@ -55,12 +56,23 @@
      */
     public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2;
 
+    /**
+     * The subscription ID associated with this operation is invalid or not active.
+     * <p>
+     * This is a configuration error and there should be no retry. The subscription used for this
+     * operation is either invalid or has become inactive. The active subscriptions can be queried
+     * with {@link SubscriptionManager#getActiveSubscriptionInfoList()}.
+     * @hide
+     */
+    public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3;
+
     /**@hide*/
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "CODE_ERROR_", value = {
             CODE_ERROR_UNSPECIFIED,
             CODE_ERROR_SERVICE_UNAVAILABLE,
-            CODE_ERROR_UNSUPPORTED_OPERATION
+            CODE_ERROR_UNSUPPORTED_OPERATION,
+            CODE_ERROR_INVALID_SUBSCRIPTION
     })
     public @interface ImsErrorCode {}
 
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index be58723..a1a7fcc 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -31,6 +31,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.SubscriptionManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -375,6 +376,13 @@
         c.setExecutor(executor);
         try {
             getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException | IllegalStateException e) {
             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -390,8 +398,6 @@
      * @param c The {@link RegistrationCallback} to be removed.
      * @see SubscriptionManager.OnSubscriptionsChangedListener
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
-     * @throws IllegalArgumentException if the subscription ID associated with this callback is
-     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
@@ -445,6 +451,13 @@
         c.setExecutor(executor);
         try {
             getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }  catch (IllegalStateException e) {
@@ -460,8 +473,6 @@
      * inactive subscription, it will result in a no-op.
      * @param c The MmTel {@link CapabilityCallback} to be removed.
      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
-     * @throws IllegalArgumentException if the subscription ID associated with this callback is
-     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
@@ -482,12 +493,9 @@
      * be enabled as long as the carrier has provisioned these services for the specified
      * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
      * carrier requirements.
-     *
-     * Modifying this value may also trigger an IMS registration or deregistration, depending on
-     * whether or not the new value is enabled or disabled.
-     *
+     * <p>
      * Note: If the carrier configuration for advanced calling is not editable or hidden, this
-     * method will do nothing and will instead always use the default value.
+     * method will always return the default value.
      *
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
@@ -495,12 +503,21 @@
      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
      * @see #setAdvancedCallingSettingEnabled(boolean)
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user's setting for advanced calling is enabled, false otherwise.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAdvancedCallingSettingEnabled() {
         try {
             return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -526,12 +543,20 @@
      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
      * @see #isAdvancedCallingSettingEnabled()
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
         try {
             getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -597,6 +622,9 @@
 
     /**
      * The user's setting for whether or not they have enabled the "Video Calling" setting.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user’s “Video Calling” setting is currently enabled.
      * @see #setVtSettingEnabled(boolean)
      */
@@ -604,6 +632,13 @@
     public boolean isVtSettingEnabled() {
         try {
             return getITelephony().isVtSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -611,13 +646,22 @@
 
     /**
      * Change the user's setting for Video Telephony and enable the Video Telephony capability.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #isVtSettingEnabled()
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVtSettingEnabled(boolean isEnabled) {
         try {
             getITelephony().setVtSettingEnabled(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -625,12 +669,22 @@
 
     /**
      * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #setVoWiFiSettingEnabled(boolean)
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isVoWiFiSettingEnabled() {
         try {
             return getITelephony().isVoWiFiSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -638,6 +692,9 @@
 
     /**
      * Sets the user's setting for whether or not Voice over WiFi is enabled.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
      * @see #isVoWiFiSettingEnabled()
      */
@@ -645,13 +702,23 @@
     public void setVoWiFiSettingEnabled(boolean isEnabled) {
         try {
             getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /**
+     * Returns the user's voice over WiFi roaming setting associated with the current subscription.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
      * if disabled.
      * @see #setVoWiFiRoamingSettingEnabled(boolean)
@@ -660,6 +727,13 @@
     public boolean isVoWiFiRoamingSettingEnabled() {
         try {
             return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -667,15 +741,24 @@
 
     /**
      * Change the user's setting for Voice over WiFi while roaming.
+     *
      * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
      *     false otherwise.
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #isVoWiFiRoamingSettingEnabled()
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
         try {
             getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -691,19 +774,31 @@
      * - {@link #WIFI_MODE_WIFI_ONLY}
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #setVoWiFiSettingEnabled(boolean)
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
         try {
             getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /**
+     * Returns the user's voice over WiFi Roaming mode setting associated with the device.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return The Voice over WiFi Mode preference set by the user, which can be one of the
      * following:
      * - {@link #WIFI_MODE_WIFI_ONLY}
@@ -715,6 +810,13 @@
     public @WiFiCallingMode int getVoWiFiModeSetting() {
         try {
             return getITelephony().getVoWiFiModeSetting(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -727,13 +829,21 @@
      * - {@link #WIFI_MODE_WIFI_ONLY}
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #getVoWiFiModeSetting()
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
         try {
             getITelephony().setVoWiFiModeSetting(mSubId, mode);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -748,12 +858,21 @@
      *     - {@link #WIFI_MODE_WIFI_ONLY}
      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #setVoWiFiRoamingSettingEnabled(boolean)
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
         try {
             return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -768,13 +887,21 @@
      *     - {@link #WIFI_MODE_WIFI_ONLY}
      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #getVoWiFiRoamingModeSetting()
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
         try {
             getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -787,13 +914,21 @@
      * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting
      * for RTT. That value is enabled/disabled separately by the user through the Accessibility
      * settings.
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @param isEnabled if true RTT should be enabled during calls made on this subscription.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRttCapabilitySetting(boolean isEnabled) {
         try {
             getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -801,6 +936,9 @@
 
     /**
      * @return true if TTY over VoLTE is supported
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see android.telecom.TelecomManager#getCurrentTtyMode
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
      */
@@ -808,6 +946,13 @@
     boolean isTtyOverVolteEnabled() {
         try {
             return getITelephony().isTtyOverVolteEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
new file mode 100644
index 0000000..3c343dd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2018 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.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.os.Binder;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manager for interfacing with the framework RCS services, including the User Capability Exchange
+ * (UCE) service, as well as managing user settings.
+ *
+ * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this manager.
+ * @hide
+ */
+public class ImsRcsManager {
+
+    /**
+     * Receives RCS availability status updates from the ImsService.
+     *
+     * @see #isAvailable(int)
+     * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+     * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     */
+    public static class AvailabilityCallback {
+
+        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+            private final AvailabilityCallback mLocalCallback;
+            private Executor mExecutor;
+
+            CapabilityBinder(AvailabilityCallback c) {
+                mLocalCallback = c;
+            }
+
+            @Override
+            public void onCapabilitiesStatusChanged(int config) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(
+                                new RcsFeature.RcsImsCapabilities(config))));
+            }
+
+            @Override
+            public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                    boolean isEnabled) {
+                // This is not used for public interfaces.
+            }
+
+            @Override
+            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                    @ImsFeature.ImsCapabilityError int reason) {
+                // This is not used for public interfaces
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+        /**
+         * The availability of the feature's capabilities has changed to either available or
+         * unavailable.
+         * <p>
+         * If unavailable, the feature does not support the capability at the current time. This may
+         * be due to network or subscription provisioning changes, such as the IMS registration
+         * being lost, network type changing, or OMA-DM provisioning updates.
+         *
+         * @param capabilities The new availability of the capabilities.
+         */
+        public void onAvailabilityChanged(RcsFeature.RcsImsCapabilities capabilities) {
+        }
+
+        /**@hide*/
+        public final IImsCapabilityCallback getBinder() {
+            return mBinder;
+        }
+
+        private void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    private final int mSubId;
+    private final Context mContext;
+
+
+    /**
+     * Create an instance of ImsRcsManager for the subscription id specified.
+     *
+     * @param context The context to create this ImsRcsManager instance within.
+     * @param subscriptionId The ID of the subscription that this ImsRcsManager will use.
+     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @hide
+     */
+    public static ImsRcsManager createForSubscriptionId(Context context, int subscriptionId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        return new ImsRcsManager(context, subscriptionId);
+    }
+
+    /**
+     * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this class.
+     */
+    private ImsRcsManager(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Registers an {@link AvailabilityCallback} with the system, which will provide RCS
+     * availability updates for the subscription specified.
+     *
+     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+     * subscription changed events and call
+     * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a
+     * subscription is removed.
+     * <p>
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current capabilities.
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The RCS {@link AvailabilityCallback} to be registered.
+     * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerRcsAvailabilityCallback(@CallbackExecutor Executor executor,
+            @NonNull AvailabilityCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+        throw new UnsupportedOperationException("registerRcsAvailabilityCallback is not"
+                + "supported.");
+    }
+
+    /**
+     * Removes an existing RCS {@link AvailabilityCallback}.
+     * <p>
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be unregistered. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     * @param c The RCS {@link AvailabilityCallback} to be removed.
+     * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+        }
+        throw new UnsupportedOperationException("unregisterRcsAvailabilityCallback is not"
+                + "supported.");
+    }
+
+    /**
+     * Query for the capability of an IMS RCS service provided by the framework.
+     * <p>
+     * This only reports the status of RCS capabilities provided by the framework, not necessarily
+     * RCS capabilities provided over-the-top by applications.
+     *
+     * @param capability The RCS capability to query.
+     * @return true if the RCS capability is capable for this subscription, false otherwise. This
+     * does not necessarily mean that we are registered for IMS and the capability is available, but
+     * rather the subscription is capable of this service over IMS.
+     * @see #isAvailable(int)
+     * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        throw new UnsupportedOperationException("isCapable is not supported.");
+    }
+
+    /**
+     * Query the availability of an IMS RCS capability.
+     * <p>
+     * This only reports the status of RCS capabilities provided by the framework, not necessarily
+     * RCS capabilities provided by over-the-top by applications.
+     *
+     * @param capability the RCS capability to query.
+     * @return true if the RCS capability is currently available for the associated subscription,
+     * false otherwise. If the capability is available, IMS is registered and the service is
+     * currently available over IMS.
+     * @see #isCapable(int)
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        throw new UnsupportedOperationException("isAvailable is not supported.");
+    }
+
+    /**
+     * @return A new {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for
+     * this subscription.
+     */
+    @NonNull
+    public RcsUceAdapter getUceAdapter() {
+        return new RcsUceAdapter(mSubId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index cc037e3..effdf48 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -387,6 +387,24 @@
         }
     }
 
+    /**
+     * Notify the framework that an RCS autoconfiguration XML file has been received for
+     * provisioning.
+     * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
+     * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+     *         before being read.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
+        if (config == null) {
+            throw new IllegalArgumentException("Must include a non-null config XML file.");
+        }
+        // TODO: Connect to ImsConfigImplBase.
+        throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not"
+                + "supported");
+    }
+
     private static boolean isImsAvailableOnDevice() {
         IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
         if (pm == null) {
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
similarity index 61%
rename from tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
rename to telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
index f04360f..bef6a40 100644
--- a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (c) 2018 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.
@@ -14,16 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.preload.classdataretrieval;
 
-import com.android.ddmlib.Client;
+package android.telephony.ims;
 
-import java.util.Map;
-
-/**
- * Retrieve a class-to-classloader map for loaded classes from the client.
- */
-public interface ClassDataRetriever {
-
-    public Map<String, String> getClassData(Client client);
-}
+parcelable RcsContactUceCapability;
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
new file mode 100644
index 0000000..492170b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2018 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.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
+ * @hide
+ */
+public final class RcsContactUceCapability implements Parcelable {
+
+    /** Supports 1-to-1 chat */
+    public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0);
+    /** Supports group chat */
+    public static final int CAPABILITY_CHAT_SESSION = (1 << 1);
+    /** Supports full store and forward group chat information. */
+    public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2);
+    /**
+     * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward.
+     */
+    public static final int CAPABILITY_FILE_TRANSFER = (1 << 3);
+    /** Supports File Transfer Thumbnail */
+    public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4);
+    /** Supports File Transfer with Store and Forward */
+    public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5);
+    /** Supports File Transfer via HTTP */
+    public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6);
+    /** Supports file transfer via SMS */
+    public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7);
+    /** Supports image sharing */
+    public static final int CAPABILITY_IMAGE_SHARE = (1 << 8);
+    /** Supports video sharing during a circuit-switch call (IR.74)*/
+    public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9);
+    /** Supports video share outside of voice call (IR.84) */
+    public static final int CAPABILITY_VIDEO_SHARE = (1 << 10);
+    /** Supports social presence information */
+    public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11);
+    /** Supports capability discovery via presence */
+    public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12);
+    /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */
+    public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13);
+    /** Supports IP video calling (IR.94) */
+    public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14);
+    /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */
+    public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15);
+    /** Supports Geolocation PUSH via SMS for fallback.  */
+    public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16);
+    /** Supports Geolocation pull. */
+    public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17);
+    /** Supports Geolocation pull using file transfer support. */
+    public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18);
+    /** Supports RCS voice calling */
+    public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19);
+    /** Supports RCS video calling */
+    public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20);
+    /** Supports RCS video calling, where video media can not be dropped */
+    public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21);
+
+    /** @hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CAPABILITY_", flag = true, value = {
+            CAPABILITY_CHAT_STANDALONE,
+            CAPABILITY_CHAT_SESSION,
+            CAPABILITY_CHAT_SESSION_STORE_FORWARD,
+            CAPABILITY_FILE_TRANSFER,
+            CAPABILITY_FILE_TRANSFER_THUMBNAIL,
+            CAPABILITY_FILE_TRANSFER_STORE_FORWARD,
+            CAPABILITY_FILE_TRANSFER_HTTP,
+            CAPABILITY_FILE_TRANSFER_SMS,
+            CAPABILITY_IMAGE_SHARE,
+            CAPABILITY_VIDEO_SHARE_DURING_CS_CALL,
+            CAPABILITY_VIDEO_SHARE,
+            CAPABILITY_SOCIAL_PRESENCE,
+            CAPABILITY_DISCOVERY_VIA_PRESENCE,
+            CAPABILITY_IP_VOICE_CALL,
+            CAPABILITY_IP_VIDEO_CALL,
+            CAPABILITY_GEOLOCATION_PUSH,
+            CAPABILITY_GEOLOCATION_PUSH_SMS,
+            CAPABILITY_GEOLOCATION_PULL,
+            CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER,
+            CAPABILITY_RCS_VOICE_CALL,
+            CAPABILITY_RCS_VIDEO_CALL,
+            CAPABILITY_RCS_VIDEO_ONLY_CALL
+    })
+    public @interface CapabilityFlag {}
+
+    /**
+     * Builder to help construct {@link RcsContactUceCapability} instances.
+     */
+    public static class Builder {
+
+        private final RcsContactUceCapability mCapabilities;
+
+        /**
+         * Create the Builder, which can be used to set UCE capabilities as well as custom
+         * capability extensions.
+         * @param contact The contact URI that the capabilities are attached to.
+         */
+        public Builder(@NonNull Uri contact) {
+            mCapabilities = new RcsContactUceCapability(contact);
+        }
+
+        /**
+         * Add a UCE capability bit-field as well as the associated URI that the framework should
+         * use for those services. This is mainly used for capabilities that may use a URI separate
+         * from the contact's URI, for example the URI to use for VT calls.
+         * @param type The capability to map to a service URI that is different from the contact's
+         *         URI.
+         */
+        public Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) {
+            mCapabilities.mCapabilities |= type;
+            // Put each of these capabilities into the map separately.
+            for (int shift = 0; shift < Integer.SIZE; shift++) {
+                int cap = type & (1 << shift);
+                if (cap != 0) {
+                    mCapabilities.mServiceMap.put(cap, serviceUri);
+                    // remove that capability from the field.
+                    type &= ~cap;
+                }
+                if (type == 0) {
+                    // no need to keep going, end early.
+                    break;
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Add a UCE capability flag that this contact supports.
+         * @param type the capability that the contact supports.
+         */
+        public Builder add(@CapabilityFlag int type) {
+            mCapabilities.mCapabilities |= type;
+            return this;
+        }
+
+        /**
+         * Add a carrier specific service tag.
+         * @param extension A string containing a carrier specific service tag that is an extension
+         *         of the {@link CapabilityFlag}s that are defined here.
+         */
+        public Builder add(@NonNull String extension) {
+            mCapabilities.mExtensionTags.add(extension);
+            return this;
+        }
+
+        /**
+         * @return the constructed instance.
+         */
+        public RcsContactUceCapability build() {
+            return mCapabilities;
+        }
+    }
+
+    private final Uri mContactUri;
+    private int mCapabilities;
+    private List<String> mExtensionTags = new ArrayList<>();
+    private Map<Integer, Uri> mServiceMap = new HashMap<>();
+
+    /**
+     * Use {@link Builder} to build an instance of this interface.
+     * @param contact The URI associated with this capability information.
+     * @hide
+     */
+    RcsContactUceCapability(@NonNull Uri contact) {
+        mContactUri = contact;
+    }
+
+    private RcsContactUceCapability(Parcel in) {
+        mContactUri = in.readParcelable(Uri.class.getClassLoader());
+        mCapabilities = in.readInt();
+        in.readStringList(mExtensionTags);
+        // read mServiceMap as key,value pair
+        int mapSize = in.readInt();
+        for (int i = 0; i < mapSize; i++) {
+            mServiceMap.put(in.readInt(), in.readParcelable(Uri.class.getClassLoader()));
+        }
+    }
+
+    public static final Creator<RcsContactUceCapability> CREATOR =
+            new Creator<RcsContactUceCapability>() {
+        @Override
+        public RcsContactUceCapability createFromParcel(Parcel in) {
+            return new RcsContactUceCapability(in);
+        }
+
+        @Override
+        public RcsContactUceCapability[] newArray(int size) {
+            return new RcsContactUceCapability[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(mContactUri, 0);
+        out.writeInt(mCapabilities);
+        out.writeStringList(mExtensionTags);
+        // write mServiceMap as key,value pairs
+        int mapSize = mServiceMap.keySet().size();
+        out.writeInt(mapSize);
+        for (int key : mServiceMap.keySet()) {
+            out.writeInt(key);
+            out.writeParcelable(mServiceMap.get(key), 0);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Query for a capability
+     * @param type The capability flag to query.
+     * @return true if the capability flag specified is set, false otherwise.
+     */
+    public boolean isCapable(@CapabilityFlag int type) {
+        return (mCapabilities & type) > 0;
+    }
+
+    /**
+     * @return true if the extension service tag is set, false otherwise.
+     */
+    public boolean isCapable(@NonNull String extensionTag) {
+        return mExtensionTags.contains(extensionTag);
+    }
+
+    /**
+     * @return An immutable list containing all of the extension tags that have been set as capable.
+     * @throws UnsupportedOperationException if this list is modified.
+     */
+    public @NonNull List<String> getCapableExtensionTags() {
+        return Collections.unmodifiableList(mExtensionTags);
+    }
+
+    /**
+     * Retrieves the {@link Uri} associated with the capability being queried.
+     * <p>
+     * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless
+     * a different service {@link Uri} was associated with this capability using
+     * {@link Builder#add(int, Uri)}.
+     *
+     * @return a String containing the {@link Uri} associated with the service tag or
+     * {@code null} if this capability is not set as capable.
+     * @see #isCapable(int)
+     */
+    public @Nullable Uri getServiceUri(@CapabilityFlag int type) {
+        Uri result = mServiceMap.getOrDefault(type, null);
+        // If the capability is capable, but does not have a service URI associated, use the default
+        // contact URI.
+        if (result == null) {
+            return isCapable(type) ? getContactUri() : null;
+        }
+        return result;
+    }
+
+    /**
+     * @return the URI representing the contact associated with the capabilities.
+     */
+    public @NonNull Uri getContactUri() {
+        return mContactUri;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
new file mode 100644
index 0000000..a6a7a84
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018 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.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages RCS User Capability Exchange for the subscription specified.
+ *
+ * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
+ * @hide
+ */
+public class RcsUceAdapter {
+
+    /**
+     * An unknown error has caused the request to fail.
+     */
+    public static final int ERROR_GENERIC_FAILURE = 1;
+    /**
+     * The carrier network does not have UCE support enabled for this subscriber.
+     */
+    public static final int ERROR_NOT_ENABLED = 2;
+    /**
+     * The data network that the device is connected to does not support UCE currently (e.g. it is
+     * 1x only currently).
+     */
+    public static final int ERROR_NOT_AVAILABLE = 3;
+    /**
+     * The network has responded with SIP 403 error and a reason "User not registered."
+     */
+    public static final int ERROR_NOT_REGISTERED = 4;
+    /**
+     * The network has responded to this request with a SIP 403 error and reason "not authorized for
+     * presence" for this subscriber.
+     */
+    public static final int ERROR_NOT_AUTHORIZED = 5;
+    /**
+     * The network has responded to this request with a SIP 403 error and no reason.
+     */
+    public static final int ERROR_FORBIDDEN = 6;
+    /**
+     * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
+     * subscriber to the carrier network.
+     */
+    public static final int ERROR_NOT_FOUND = 7;
+    /**
+     * The capabilities request contained too many URIs for the carrier network to handle. Retry
+     * with a lower number of contact numbers. The number varies per carrier.
+     */
+    // TODO: Try to integrate this into the API so that the service will split based on carrier.
+    public static final int ERROR_REQUEST_TOO_LARGE = 8;
+    /**
+     * The network did not respond to the capabilities request before the request timed out.
+     */
+    public static final int ERROR_REQUEST_TIMEOUT = 10;
+    /**
+     * The request failed due to the service having insufficient memory.
+     */
+    public static final int ERROR_INSUFFICIENT_MEMORY = 11;
+    /**
+     * The network was lost while trying to complete the request.
+     */
+    public static final int ERROR_LOST_NETWORK = 12;
+    /**
+     * The request has failed because the same request has already been added to the queue.
+     */
+    public static final int ERROR_ALREADY_IN_QUEUE = 13;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "ERROR_", value = {
+            ERROR_GENERIC_FAILURE,
+            ERROR_NOT_ENABLED,
+            ERROR_NOT_AVAILABLE,
+            ERROR_NOT_REGISTERED,
+            ERROR_NOT_AUTHORIZED,
+            ERROR_FORBIDDEN,
+            ERROR_NOT_FOUND,
+            ERROR_REQUEST_TOO_LARGE,
+            ERROR_REQUEST_TIMEOUT,
+            ERROR_INSUFFICIENT_MEMORY,
+            ERROR_LOST_NETWORK,
+            ERROR_ALREADY_IN_QUEUE
+    })
+    public @interface ErrorCode {}
+
+    /**
+     * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
+     * UCE.
+     */
+    public static final int PUBLISH_STATE_200_OK = 1;
+
+    /**
+     * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
+     */
+    public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
+
+    /**
+     * The device has tried to publish its capabilities, which has resulted in an error. This error
+     * is related to the fact that the device is not VoLTE provisioned.
+     */
+    public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3;
+
+    /**
+     * The device has tried to publish its capabilities, which has resulted in an error. This error
+     * is related to the fact that the device is not RCS or UCE provisioned.
+     */
+    public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
+
+    /**
+     * The last publish resulted in a "408 Request Timeout" response.
+     */
+    public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
+
+    /**
+     * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable"
+     * or SIP 423 - "Interval too short".
+     * <p>
+     * Device shall retry with exponential back-off.
+     */
+    public static final int PUBLISH_STATE_OTHER_ERROR = 6;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "PUBLISH_STATE_", value = {
+            PUBLISH_STATE_200_OK,
+            PUBLISH_STATE_NOT_PUBLISHED,
+            PUBLISH_STATE_VOLTE_PROVISION_ERROR,
+            PUBLISH_STATE_RCS_PROVISION_ERROR,
+            PUBLISH_STATE_REQUEST_TIMEOUT,
+            PUBLISH_STATE_OTHER_ERROR
+    })
+    public @interface PublishState {}
+
+
+    /**
+     * Provides a one-time callback for the response to a UCE request. After this callback is called
+     * by the framework, the reference to this callback will be discarded on the service side.
+     * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+     */
+    public static class CapabilitiesCallback {
+
+        /**
+         * Notify this application that the pending capability request has returned successfully.
+         * @param contactCapabilities List of capabilities associated with each contact requested.
+         */
+        public void onCapabilitiesReceived(
+                @NonNull List<RcsContactUceCapability> contactCapabilities) {
+
+        }
+
+        /**
+         * The pending request has resulted in an error and may need to be retried, depending on the
+         * error code.
+         * @param errorCode The reason for the framework being unable to process the request.
+         */
+        public void onError(@ErrorCode int errorCode) {
+
+        }
+    }
+
+    private final int mSubId;
+
+    /**
+     * Not to be instantiated directly, use
+     * {@link ImsRcsManager#createForSubscriptionId(Context, int)} and
+     * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class.
+     */
+    RcsUceAdapter(int subId) {
+        mSubId = subId;
+    }
+
+    /**
+     * Request the User Capability Exchange capabilities for one or more contacts.
+     * <p>
+     * Be sure to check the availability of this feature using
+     * {@link ImsRcsManager#isAvailable(int)} and ensuring
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
+     * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
+     *
+     * @param executor The executor that will be used when the request is completed and the
+     *         {@link CapabilitiesCallback} is called.
+     * @param contactNumbers A list of numbers that the capabilities are being requested for.
+     * @param c A one-time callback for when the request for capabilities completes or there is an
+     *         error processing the request.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void requestCapabilities(@CallbackExecutor Executor executor,
+            @NonNull List<Uri> contactNumbers,
+            @NonNull CapabilitiesCallback c) throws ImsException {
+        throw new UnsupportedOperationException("isUceSettingEnabled is not supported.");
+    }
+
+    /**
+     * Gets the last publish result from the UCE service if the device is using an RCS presence
+     * server.
+     * @return The last publish result from the UCE service. If the device is using SIP OPTIONS,
+     * this method will return {@link #PUBLISH_STATE_200_OK} as well.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @PublishState int getUcePublishState() throws ImsException {
+        throw new UnsupportedOperationException("getPublishState is not supported.");
+    }
+
+    /**
+     * The user’s setting for whether or not Presence and User Capability Exchange (UCE) is enabled
+     * for the associated subscription.
+     *
+     * @return true if the user’s setting for UCE is enabled, false otherwise. If false,
+     * {@link ImsRcsManager#isCapable(int)} will return false for
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE}
+     * @see #setUceSettingEnabled(boolean)
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isUceSettingEnabled() throws ImsException {
+        // TODO: add SubscriptionController column for this property.
+        throw new UnsupportedOperationException("isUceSettingEnabled is not supported.");
+    }
+    /**
+     * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
+     * @param isEnabled the user's setting for whether or not they wish for Presence and User
+     *         Capability Exchange to be enabled. If false,
+     *         {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
+     *         {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} capability will be
+     *         disabled, depending on which type of UCE the carrier supports.
+     * @see #isUceSettingEnabled()
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
+        // TODO: add SubscriptionController column for this property.
+        throw new UnsupportedOperationException("setUceSettingEnabled is not supported.");
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 4433c1c..53e4596 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -17,6 +17,8 @@
 
 package android.telephony.ims.aidl;
 
+import android.os.PersistableBundle;
+
 import android.telephony.ims.aidl.IImsConfigCallback;
 
 import com.android.ims.ImsConfigListener;
@@ -37,4 +39,5 @@
     int setConfigInt(int item, int value);
     // Return result code defined in ImsConfig#OperationStatusConstants
     int setConfigString(int item, String value);
+    void updateImsCarrierConfigs(in PersistableBundle bundle);
 }
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 74af6bf..8f89899 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -34,7 +34,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
 
@@ -106,6 +108,16 @@
     public static final int FEATURE_MAX = 3;
 
     /**
+     * Used for logging purposes.
+     * @hide
+     */
+    public static final Map<Integer, String> FEATURE_LOG_MAP = new HashMap<Integer, String>() {{
+            put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL");
+            put(FEATURE_MMTEL, "MMTEL");
+            put(FEATURE_RCS, "RCS");
+        }};
+
+    /**
      * Integer values defining IMS features that are supported in ImsFeature.
      * @hide
      */
@@ -132,19 +144,34 @@
     public @interface ImsState {}
 
     /**
-     * This {@link ImsFeature}'s state is unavailable and should not be communicated with.
+     * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will
+     * remove all bindings back to the framework. Any attempt to communicate with the framework
+     * during this time will result in an {@link IllegalStateException}.
      */
     public static final int STATE_UNAVAILABLE = 0;
     /**
-     * This {@link ImsFeature} state is initializing and should not be communicated with.
+     * This {@link ImsFeature} state is initializing and should not be communicated with. This will
+     * remove all bindings back to the framework. Any attempt to communicate with the framework
+     * during this time will result in an {@link IllegalStateException}.
      */
     public static final int STATE_INITIALIZING = 1;
     /**
-     * This {@link ImsFeature} is ready for communication.
+     * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods
+     * until {@link #onFeatureReady()} is called.
      */
     public static final int STATE_READY = 2;
 
     /**
+     * Used for logging purposes.
+     * @hide
+     */
+    public static final Map<Integer, String> STATE_LOG_MAP = new HashMap<Integer, String>() {{
+            put(STATE_UNAVAILABLE, "UNAVAILABLE");
+            put(STATE_INITIALIZING, "INITIALIZING");
+            put(STATE_READY, "READY");
+        }};
+
+    /**
      * Integer values defining the result codes that should be returned from
      * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
      * @hide
@@ -208,11 +235,14 @@
 
     /**
      * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
+     * <p>
+     * Typically this class is not used directly, but rather extended in subclasses of
+     * {@link ImsFeature} to provide service specific capabilities.
      * @hide
-     * @deprecated Use {@link MmTelFeature.MmTelCapabilities} instead.
      */
-    @SystemApi  // SystemApi only because it was leaked through type usage in a previous release.
+    @SystemApi
     public static class Capabilities {
+        /** @deprecated Use getters and accessors instead. */
         protected int mCapabilities = 0;
 
         /**
@@ -305,12 +335,12 @@
     /** @hide */
     protected final Object mLock = new Object();
 
-    private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
-            new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+    private final Set<IImsFeatureStatusCallback> mStatusCallbacks =
+            Collections.newSetFromMap(new WeakHashMap<>());
     private @ImsState int mState = STATE_UNAVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
-            = new RemoteCallbackList<>();
+    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks =
+            new RemoteCallbackList<>();
     private Capabilities mCapabilityStatus = new Capabilities();
 
     /**
@@ -322,6 +352,16 @@
     }
 
     /**
+     * @return The SIM slot index associated with this ImsFeature.
+     *
+     * @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the
+     * subscription IDs associated with this slot.
+     */
+    public final int getSlotIndex() {
+        return mSlotId;
+    }
+
+    /**
      * @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE},
      * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
      * @hide
@@ -490,7 +530,9 @@
     public abstract void onFeatureRemoved();
 
     /**
-     * Called when the feature has been initialized and communication with the framework is set up.
+     * Called after this ImsFeature has been initialized and has been set to the
+     * {@link ImsState#STATE_READY} state.
+     * <p>
      * Any attempt by this feature to access the framework before this method is called will return
      * with an {@link IllegalStateException}.
      * The IMS provider should use this method to trigger registration for this feature on the IMS
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index a637e16..5fae3ee 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -16,8 +16,14 @@
 
 package android.telephony.ims.feature;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+import android.telephony.ims.stub.RcsSipOptionsImplBase;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -32,18 +38,165 @@
         // Empty Default Implementation.
     };
 
+    /**
+     * Contains the capabilities defined and supported by a {@link RcsFeature} in the
+     * form of a bitmask. The capabilities that are used in the RcsFeature are
+     * defined as:
+     * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+     * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+     *
+     * The enabled capabilities of this RcsFeature will be set by the framework
+     * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+     * After the capabilities have been set, the RcsFeature may then perform the necessary bring up
+     * of the capability and notify the capability status as true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+     * framework that the capability is available for usage.
+     * @hide
+     */
+    public static class RcsImsCapabilities extends Capabilities {
+        /** @hide*/
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
+                CAPABILITY_TYPE_OPTIONS_UCE,
+                CAPABILITY_TYPE_PRESENCE_UCE
+        })
+        public @interface RcsImsCapabilityFlag {}
 
-    public RcsFeature() {
-        super();
+        /**
+         * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+         * If not set, this RcsFeature should not service capability requests.
+         * @hide
+         */
+        public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+        /**
+         * This carrier supports User Capability Exchange using a presence server as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using a presence
+         * server. If not set, this RcsFeature should not publish capabilities or service capability
+         * requests using presence.
+         * @hide
+         */
+        public static final int CAPABILITY_TYPE_PRESENCE_UCE =  1 << 1;
+
+        /**@hide*/
+        public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
+
+        }
+
+        /**@hide*/
+        @Override
+        public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
+
+        }
+
+        /**@hide*/
+        @Override
+        public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
+
+        }
+
+        /**@hide*/
+        @Override
+        public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
+            return false;
+        }
+    }
+    /**
+     * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
+     * set, the {@link RcsFeature} has brought up the capability and is ready for framework
+     * requests. To change the status of the capabilities
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
+     * @hide
+     */
+    @Override
+    public final RcsImsCapabilities queryCapabilityStatus() {
+        throw new UnsupportedOperationException();
     }
 
     /**
-     * {@inheritDoc}
+     * Notify the framework that the capabilities status has changed. If a capability is enabled,
+     * this signals to the framework that the capability has been initialized and is ready.
+     * Call {@link #queryCapabilityStatus()} to return the current capability status.
+     * @hide
+     */
+    public final void notifyCapabilitiesStatusChanged(RcsImsCapabilities c) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Provides the RcsFeature with the ability to return the framework capability configuration set
+     * by the framework. When the framework calls
+     * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
+     * enable or disable capability A, this method should return the correct configuration for
+     * capability A afterwards (until it has changed).
+     * @hide
+     */
+    public boolean queryCapabilityConfiguration(
+            @RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        throw new UnsupportedOperationException();
+    }
+    /**
+     * Called from the framework when the {@link RcsImsCapabilities} that have been configured for
+     * this {@link RcsFeature} has changed.
+     * <p>
+     * For each newly enabled capability flag, the corresponding capability should be brought up in
+     * the {@link RcsFeature} and registered on the network. For each newly disabled capability
+     * flag, the corresponding capability should be brought down, and deregistered. Once a new
+     * capability has been initialized and is ready for usage, the status of that capability should
+     * also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This
+     * will notify the framework that the capability is ready.
+     * <p>
+     * If for some reason one or more of these capabilities can not be enabled/disabled,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
+     * be called for each capability change that resulted in an error.
+     * @hide
      */
     @Override
     public void changeEnabledCapabilities(CapabilityChangeRequest request,
             CapabilityCallbackProxy c) {
-        // Do nothing for base implementation.
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}.
+     * <p>
+     * Will only be requested by the framework if capability exchange via SIP OPTIONS is
+     * configured as capable during a
+     * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+     * operation and the RcsFeature sets the status of the capability to true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+     *
+     * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if
+     * it is supported by the device.
+     * @hide
+     */
+    public RcsSipOptionsImplBase getOptionsExchangeImpl() {
+        // Base Implementation, override to implement functionality
+        return new RcsSipOptionsImplBase();
+    }
+
+    /**
+     * Retrieve the implementation of UCE presence for this {@link RcsFeature}.
+     * Will only be requested by the framework if presence exchang is configured as capable during
+     * a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+     * operation and the RcsFeature sets the status of the capability to true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+     *
+     * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence
+     * exchange if it is supported by the device.
+     * @hide
+     */
+    public RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
+        // Base Implementation, override to implement functionality.
+        return new RcsPresenceExchangeImplBase();
+    }
+
+    /**
+     * Construct a new {@link RcsFeature} instance.
+     */
+    public RcsFeature() {
+        super();
     }
 
     /**{@inheritDoc}*/
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 321bfff..3e135cc 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.os.PersistableBundle;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.telephony.ims.aidl.IImsConfig;
@@ -182,6 +183,11 @@
             return retVal;
         }
 
+        @Override
+        public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
+            getImsConfigImpl().updateImsCarrierConfigs(bundle);
+        }
+
         private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
             ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
             if (ref == null) {
@@ -342,6 +348,17 @@
     }
 
     /**
+     * The framework has received an RCS autoconfiguration XML file for provisioning.
+     *
+     * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format.
+     * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+     *         before being read.
+     * @hide
+     */
+    public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
+    }
+
+    /**
      * Sets the configuration value for this ImsService.
      *
      * @param item an integer key.
@@ -387,4 +404,11 @@
         // Base Implementation - To be overridden.
         return null;
     }
+
+    /**
+     * @hide
+     */
+    public void updateImsCarrierConfigs(PersistableBundle bundle) {
+        // Base Implementation - Should be overridden
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
index dfb6e2c..1a839fc 100644
--- a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -21,7 +21,6 @@
 import android.os.Parcelable;
 import android.telephony.ims.feature.ImsFeature;
 import android.util.ArraySet;
-import android.util.Pair;
 
 import java.util.Set;
 
@@ -80,7 +79,7 @@
 
         @Override
         public String toString() {
-            return "{s=" + slotId + ", f=" + featureType + "}";
+            return "{s=" + slotId + ", f=" + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "}";
         }
     }
 
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
new file mode 100644
index 0000000..289fd4c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 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.telephony.ims.stub;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class for different types of Capability exchange, presence using
+ * {@link RcsPresenceExchangeImplBase} and SIP OPTIONS exchange using {@link RcsSipOptionsImplBase}.
+ *
+ * @hide
+ */
+public class RcsCapabilityExchange {
+
+    /**  Service is unknown. */
+    public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0;
+    /** The command completed successfully. */
+    public static final int COMMAND_CODE_SUCCESS = 1;
+    /** The command failed with an unknown error. */
+    public static final int COMMAND_CODE_GENERIC_FAILURE = 2;
+    /**  Invalid parameter(s). */
+    public static final int COMMAND_CODE_INVALID_PARAM = 3;
+    /**  Fetch error. */
+    public static final int COMMAND_CODE_FETCH_ERROR = 4;
+    /**  Request timed out. */
+    public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5;
+    /**  Failure due to insufficient memory available. */
+    public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6;
+    /**  Network connection is lost. */
+    public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7;
+    /**  Requested feature/resource is not supported. */
+    public static final int COMMAND_CODE_NOT_SUPPORTED = 8;
+    /**  Contact or resource is not found. */
+    public static final int COMMAND_CODE_NOT_FOUND = 9;
+    /**  Service is not available. */
+    public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10;
+    /**  No Change in Capabilities */
+    public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11;
+
+    /** @hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "COMMAND_CODE_", value = {
+            COMMAND_CODE_SERVICE_UNKNOWN,
+            COMMAND_CODE_SUCCESS,
+            COMMAND_CODE_GENERIC_FAILURE,
+            COMMAND_CODE_INVALID_PARAM,
+            COMMAND_CODE_FETCH_ERROR,
+            COMMAND_CODE_REQUEST_TIMEOUT,
+            COMMAND_CODE_INSUFFICIENT_MEMORY,
+            COMMAND_CODE_LOST_NETWORK_CONNECTION,
+            COMMAND_CODE_NOT_SUPPORTED,
+            COMMAND_CODE_NOT_FOUND,
+            COMMAND_CODE_SERVICE_UNAVAILABLE,
+            COMMAND_CODE_NO_CHANGE_IN_CAP
+    })
+    public @interface CommandCode {}
+
+    /**
+     * Provides the framework with an update as to whether or not a command completed successfully
+     * locally. This includes capabilities requests and updates from the network. If it does not
+     * complete successfully, then the framework may retry the command again later, depending on the
+     * error. If the command does complete successfully, the framework will then wait for network
+     * updates.
+     *
+     * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further
+     *             updates will be sent for this command using the associated operationToken.
+     * @param operationToken the token associated with the pending command.
+     */
+    public final void onCommandUpdate(@CommandCode int code, int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
new file mode 100644
index 0000000..4402470
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 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.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Base implementation for RCS User Capability Exchange using Presence. Any ImsService implementing
+ * this service must implement the stub methods {@link #requestCapabilities(List, int)}  and
+ * {@link #updateCapabilities(RcsContactUceCapability, int)}.
+ *
+ * @hide
+ */
+public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
+
+    private static final String LOG_TAG = "RcsPresenceExchangeIB";
+
+    /**
+     * The request has resulted in any other 4xx/5xx/6xx that is not covered below. No retry will be
+     * attempted.
+     */
+    public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1;
+
+    /**
+     * The request has succeeded with a “200” message from the network.
+     */
+    public static final int RESPONSE_SUCCESS = 0;
+
+    /**
+     * The request has resulted in a “403” (User Not Registered) error from the network. Will retry
+     * capability polling with an exponential backoff.
+     */
+    public static final int RESPONSE_NOT_REGISTERED = 1;
+
+    /**
+     * The request has resulted in a “403” (not authorized (Requestor)) error from the network. No
+     * retry will be attempted.
+     */
+    public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2;
+
+    /**
+     * The request has resulted in a "403” (Forbidden) or other “403” error from the network and
+     * will be handled the same as “404” Not found. No retry will be attempted.
+     */
+    public static final int RESPONSE_FORBIDDEN = 3;
+
+    /**
+     * The request has resulted in a “404” (Not found) result from the network. No retry will be
+     * attempted.
+     */
+    public static final int RESPONSE_NOT_FOUND = 4;
+
+    /**
+     * The request has resulted in a “408” response. Retry after exponential backoff.
+     */
+    public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5;
+
+    /**
+     *  The network has responded with a “413” (Too Large) response from the network. Capability
+     *  request contains too many items and must be shrunk before the request will be accepted.
+     */
+    public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6;
+
+    /**
+     * The request has resulted in a “423” response. Retry after exponential backoff.
+     */
+    public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7;
+
+    /**
+     * The request has resulted in a “503” response. Retry after exponential backoff.
+     */
+    public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8;
+
+    /** @hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "RESPONSE_", value = {
+            RESPONSE_SUBSCRIBE_GENERIC_FAILURE,
+            RESPONSE_SUCCESS,
+            RESPONSE_NOT_REGISTERED,
+            RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE,
+            RESPONSE_FORBIDDEN,
+            RESPONSE_NOT_FOUND,
+            RESPONSE_SIP_REQUEST_TIMEOUT,
+            RESPONSE_SUBSCRIBE_TOO_LARGE,
+            RESPONSE_SIP_INTERVAL_TOO_SHORT,
+            RESPONSE_SIP_SERVICE_UNAVAILABLE
+    })
+    public @interface PresenceResponseCode {}
+
+    /**
+     * Provide the framework with a subsequent network response update to
+     * {@link #updateCapabilities(RcsContactUceCapability, int)} and
+     * {@link #requestCapabilities(List, int)} operations.
+     * @param code The SIP response code sent from the network for the operation token specified.
+     * @param reason The optional reason response from the network. If the network provided no
+     *         reason with the code, the string should be empty.
+     * @param operationToken The token associated with the operation this service is providing a
+     *         response for.
+     */
+    public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason,
+            int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Provides the framework with the requested contacts’ capabilities requested by the framework
+     * using {@link #requestCapabilities(List, int)} .
+     */
+    public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos,
+            int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Trigger the framework to provide a capability update using
+     * {@link #updateCapabilities(RcsContactUceCapability, int)}. This is typically used when trying
+     * to generate an initial PUBLISH for a new subscription to the network.
+     * <p>
+     * The device will cache all presence publications after boot until this method is called once.
+     */
+    public final void onNotifyUpdateCapabilites() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Notify the framework that the device’s capabilities have been unpublished from the network.
+     */
+    public final void onUnpublish() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * The user capabilities of one or multiple contacts have been requested.
+     * <p>
+     * This must be followed up with one call to {@link #onCommandUpdate(int, int)} with an update
+     * as to whether or not the command completed as well as subsequent network
+     * updates using {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
+     * {@link #onCapabilityRequestResponse(List, int)}  should be called with
+     * the presence information for the contacts specified.
+     * @param uris A {@link List} of the URIs that the framework is requesting the UCE capabilities
+     *          for.
+     * @param operationToken The token associated with this operation. Updates to this request using
+     *         {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and
+     *         {@link #onCapabilityRequestResponse(List, int)}  must use the same operation token
+     *         in response.
+     */
+    public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "requestCapabilities called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+
+    /**
+     * The capabilities of this device have been updated and should be published
+     * to the network. The framework will expect one {@link #onCommandUpdate(int, int)} call to
+     * indicate whether or not this operation failed first as well as network response
+     * updates to this update using {@link #onNetworkResponse(int, String, int)}.
+     * @param capabilities The capabilities for this device.
+     * @param operationToken The token associated with this operation. Any subsequent
+     *         {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)}
+     *         calls regarding this update must use the same token.
+     */
+    public void updateCapabilities(@NonNull RcsContactUceCapability capabilities,
+            int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "updateCapabilities called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
new file mode 100644
index 0000000..3343074
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018 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.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for RCS User Capability Exchange using SIP OPTIONS.
+ *
+ * @hide
+ */
+public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
+
+    private static final String LOG_TAG = "RcsSipOptionsImplBase";
+
+    /**
+     * Indicates a SIP response from the remote user other than 200, 480, 408, 404, or 604.
+     */
+    public static final int RESPONSE_GENERIC_FAILURE = -1;
+
+    /**
+     * Indicates that the remote user responded with a 200 OK response.
+     */
+    public static final int RESPONSE_SUCCESS = 0;
+
+    /**
+     * Indicates that the remote user responded with a 480 TEMPORARY UNAVAILABLE response.
+     */
+    public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1;
+
+    /**
+     * Indicates that the remote user responded with a 408 REQUEST TIMEOUT response.
+     */
+    public static final int RESPONSE_REQUEST_TIMEOUT = 2;
+
+    /**
+     * Indicates that the remote user responded with a 404 NOT FOUND response.
+     */
+    public static final int RESPONSE_NOT_FOUND = 3;
+
+    /**
+     * Indicates that the remote user responded with a 604 DOES NOT EXIST ANYWHERE response.
+     */
+    public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4;
+
+    /** @hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "RESPONSE_", value = {
+            RESPONSE_GENERIC_FAILURE,
+            RESPONSE_SUCCESS,
+            RESPONSE_TEMPORARILY_UNAVAILABLE,
+            RESPONSE_REQUEST_TIMEOUT,
+            RESPONSE_NOT_FOUND,
+            RESPONSE_DOES_NOT_EXIST_ANYWHERE
+    })
+    public @interface SipResponseCode {}
+
+    /**
+     * Send the response of a SIP OPTIONS capability exchange to the framework. If {@code code} is
+     * {@link #RESPONSE_SUCCESS}, info must be non-null.
+     * @param code The SIP response code that was sent by the network in response to the request
+     *        sent by {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     * @param reason The optional SIP response reason sent by the network. If none was sent, this
+     *        should be an empty string.
+     * @param info the contact's UCE capabilities associated with the capability request.
+     * @param operationToken The token associated with the original capability request, set by
+     *        {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     */
+    public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason,
+            @Nullable RcsContactUceCapability info, int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Inform the framework of a query for this device's UCE capabilities.
+     * <p>
+     * The framework will respond via the
+     * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)} or
+     * {@link #respondToCapabilityRequestWithError(Uri, int, String, int)} method.
+     * @param contactUri The URI associated with the remote contact that is requesting capabilities.
+     * @param remoteInfo The remote contact's capability information.
+     * @param operationToken An unique operation token that you have generated that will be returned
+     *         by the framework in
+     *         {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}.
+     */
+    public final void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull RcsContactUceCapability remoteInfo, int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
+     * in order to receive the capabilities of the remote user in response.
+     * <p>
+     * The implementer must call
+     * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} to send the
+     * response of this query back to the framework.
+     * @param contactUri The URI of the remote user that we wish to get the capabilities of.
+     * @param capabilities The capabilities of this device to send to the remote user.
+     * @param operationToken A token generated by the framework that will be passed through
+     * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} when this
+     *         operation has succeeded.
+     */
+    public void sendCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull RcsContactUceCapability capabilities, int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+
+    /**
+     * Respond to a remote capability request from the contact specified with the capabilities of
+     * this device.
+     * <p>
+     * The framework will use the same token and uri as what was passed in to
+     * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     * @param contactUri The URI of the remote contact.
+     * @param ownCapabilities The capabilities of this device.
+     * @param operationToken The token generated by the framework that this service obtained when
+     *         {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called.
+     */
+    public void respondToCapabilityRequest(@NonNull String contactUri,
+            @NonNull RcsContactUceCapability ownCapabilities, int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+
+    /**
+     * Respond to a remote capability request from the contact specified with the specified error.
+     * <p>
+     * The framework will use the same token and uri as what was passed in to
+     * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     * @param contactUri A URI containing the remote contact.
+     * @param code The SIP response code to respond with.
+     * @param reason A non-null String containing the reason associated with the SIP code.
+     * @param operationToken The token provided by the framework when
+     *         {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called.
+     *
+     */
+    public void respondToCapabilityRequestWithError(@NonNull Uri contactUri,
+            @SipResponseCode int code, @NonNull String reason, int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+}
diff --git a/test-base/Android.mk b/test-base/Android.mk
deleted file mode 100644
index a9d30cf3..0000000
--- a/test-base/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-ifeq ($(HOST_OS),linux)
-# Build the legacy-performance-test-hostdex library
-# =================================================
-# This contains the android.test.PerformanceTestCase class only
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
-LOCAL_MODULE := legacy-performance-test-hostdex
-
-include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
-endif  # HOST_OS == linux
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript
similarity index 100%
rename from tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
rename to tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 5260b30..502aa97 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -56,6 +56,7 @@
         "androidx.test.rules",
         "FrameworksNetCommonTests",
         "frameworks-base-testutils",
+        "frameworks-net-integration-testutils",
         "framework-protos",
         "mockito-target-minus-junit4",
         "net-tests-utils",
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
new file mode 100644
index 0000000..16a68d7
--- /dev/null
+++ b/tests/net/integration/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2019 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.
+//
+
+// Utilities for testing framework code both in integration and unit tests.
+java_library {
+    name: "frameworks-net-integration-testutils",
+    srcs: ["util/**/*.java", "util/**/*.kt"],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.rules",
+        "junit",
+        "net-tests-utils",
+    ],
+    libs: [
+        "services.core",
+        "services.net",
+    ],
+}
diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
new file mode 100644
index 0000000..fa2b99c
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server
+
+import android.net.ConnectivityManager.TYPE_BLUETOOTH
+import android.net.ConnectivityManager.TYPE_ETHERNET
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_NONE
+import android.net.ConnectivityManager.TYPE_TEST
+import android.net.ConnectivityManager.TYPE_VPN
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkCapabilities.TRANSPORT_TEST
+import android.net.NetworkCapabilities.TRANSPORT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+
+fun transportToLegacyType(transport: Int) = when (transport) {
+    TRANSPORT_BLUETOOTH -> TYPE_BLUETOOTH
+    TRANSPORT_CELLULAR -> TYPE_MOBILE
+    TRANSPORT_ETHERNET -> TYPE_ETHERNET
+    TRANSPORT_TEST -> TYPE_TEST
+    TRANSPORT_VPN -> TYPE_VPN
+    TRANSPORT_WIFI -> TYPE_WIFI
+    else -> TYPE_NONE
+}
\ No newline at end of file
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
new file mode 100644
index 0000000..1e8d83c
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+
+import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+import android.net.NetworkFactory;
+import android.net.NetworkInfo;
+import android.net.NetworkMisc;
+import android.net.NetworkSpecifier;
+import android.net.SocketKeepalive;
+import android.net.UidRange;
+import android.os.ConditionVariable;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.server.connectivity.ConnectivityConstants;
+import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TestableNetworkCallback;
+
+import java.util.Set;
+
+public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
+    private final NetworkInfo mNetworkInfo;
+    private final NetworkCapabilities mNetworkCapabilities;
+    private final HandlerThread mHandlerThread;
+    private final Context mContext;
+    private final String mLogTag;
+
+    private final ConditionVariable mDisconnected = new ConditionVariable();
+    private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
+    private int mScore;
+    private NetworkAgent mNetworkAgent;
+    private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
+    private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
+    private Integer mExpectedKeepaliveSlot = null;
+
+    public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
+            throws Exception {
+        final int type = transportToLegacyType(transport);
+        final String typeName = ConnectivityManager.getNetworkTypeName(type);
+        mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
+        mNetworkCapabilities = new NetworkCapabilities();
+        mNetworkCapabilities.addTransportType(transport);
+        switch (transport) {
+            case TRANSPORT_ETHERNET:
+                mScore = 70;
+                break;
+            case TRANSPORT_WIFI:
+                mScore = 60;
+                break;
+            case TRANSPORT_CELLULAR:
+                mScore = 50;
+                break;
+            case TRANSPORT_WIFI_AWARE:
+                mScore = 20;
+                break;
+            case TRANSPORT_VPN:
+                mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
+                mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
+                break;
+            default:
+                throw new UnsupportedOperationException("unimplemented network type");
+        }
+        mContext = context;
+        mLogTag = "Mock-" + typeName;
+        mHandlerThread = new HandlerThread(mLogTag);
+        mHandlerThread.start();
+
+        mNetworkAgent = makeNetworkAgent(linkProperties);
+    }
+
+    protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties)
+            throws Exception {
+        return new InstrumentedNetworkAgent(this, linkProperties);
+    }
+
+    public static class InstrumentedNetworkAgent extends NetworkAgent {
+        private final NetworkAgentWrapper mWrapper;
+
+        public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) {
+            super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag,
+                    wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore,
+                    new NetworkMisc(), NetworkFactory.SerialNumber.NONE);
+            mWrapper = wrapper;
+        }
+
+        @Override
+        public void unwanted() {
+            mWrapper.mDisconnected.open();
+        }
+
+        @Override
+        public void startSocketKeepalive(Message msg) {
+            int slot = msg.arg1;
+            if (mWrapper.mExpectedKeepaliveSlot != null) {
+                assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
+            }
+            onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError);
+        }
+
+        @Override
+        public void stopSocketKeepalive(Message msg) {
+            onSocketKeepaliveEvent(msg.arg1, mWrapper.mStopKeepaliveError);
+        }
+
+        @Override
+        protected void preventAutomaticReconnect() {
+            mWrapper.mPreventReconnectReceived.open();
+        }
+
+        @Override
+        protected void addKeepalivePacketFilter(Message msg) {
+            Log.i(mWrapper.mLogTag, "Add keepalive packet filter.");
+        }
+
+        @Override
+        protected void removeKeepalivePacketFilter(Message msg) {
+            Log.i(mWrapper.mLogTag, "Remove keepalive packet filter.");
+        }
+    }
+
+    public void adjustScore(int change) {
+        mScore += change;
+        mNetworkAgent.sendNetworkScore(mScore);
+    }
+
+    public int getScore() {
+        return mScore;
+    }
+
+    public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
+        mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated);
+    }
+
+    public void addCapability(int capability) {
+        mNetworkCapabilities.addCapability(capability);
+        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+    }
+
+    public void removeCapability(int capability) {
+        mNetworkCapabilities.removeCapability(capability);
+        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+    }
+
+    public void setUids(Set<UidRange> uids) {
+        mNetworkCapabilities.setUids(uids);
+        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+    }
+
+    public void setSignalStrength(int signalStrength) {
+        mNetworkCapabilities.setSignalStrength(signalStrength);
+        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+    }
+
+    public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+        mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
+        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+    }
+
+    public void setNetworkCapabilities(NetworkCapabilities nc, boolean sendToConnectivityService) {
+        mNetworkCapabilities.set(nc);
+        if (sendToConnectivityService) {
+            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+        }
+    }
+
+    public void connect() {
+        assertNotEquals("MockNetworkAgents can only be connected once",
+                getNetworkInfo().getDetailedState(), NetworkInfo.DetailedState.CONNECTED);
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+    }
+
+    public void suspend() {
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
+        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+    }
+
+    public void resume() {
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+    }
+
+    public void disconnect() {
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+    }
+
+    @Override
+    public Network getNetwork() {
+        return new Network(mNetworkAgent.netId);
+    }
+
+    public void expectPreventReconnectReceived(long timeoutMs) {
+        assertTrue(mPreventReconnectReceived.block(timeoutMs));
+    }
+
+    public void expectDisconnected(long timeoutMs) {
+        assertTrue(mDisconnected.block(timeoutMs));
+    }
+
+    public void sendLinkProperties(LinkProperties lp) {
+        mNetworkAgent.sendLinkProperties(lp);
+    }
+
+    public void setStartKeepaliveEvent(int reason) {
+        mStartKeepaliveError = reason;
+    }
+
+    public void setStopKeepaliveEvent(int reason) {
+        mStopKeepaliveError = reason;
+    }
+
+    public void setExpectedKeepaliveSlot(Integer slot) {
+        mExpectedKeepaliveSlot = slot;
+    }
+
+    public NetworkAgent getNetworkAgent() {
+        return mNetworkAgent;
+    }
+
+    public NetworkInfo getNetworkInfo() {
+        return mNetworkInfo;
+    }
+
+    public NetworkCapabilities getNetworkCapabilities() {
+        return mNetworkCapabilities;
+    }
+
+    public void waitForIdle(long timeoutMs) {
+        HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs);
+    }
+}
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
new file mode 100644
index 0000000..eb290dc
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server
+
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * A [NetIdManager] that generates ID starting from [NetIdManager.MAX_NET_ID] and decreasing, rather
+ * than starting from [NetIdManager.MIN_NET_ID] and increasing.
+ *
+ * Useful for testing ConnectivityService, to minimize the risk of test ConnectivityService netIDs
+ * overlapping with netIDs used by the real ConnectivityService on the device.
+ *
+ * IDs may still overlap if many networks have been used on the device (so the "real" netIDs
+ * are close to MAX_NET_ID), but this is typically not the case when running unit tests. Also, there
+ * is no way to fully solve the overlap issue without altering ID allocation in non-test code, as
+ * the real ConnectivityService could start using netIds that have been used by the test in the
+ * past.
+ */
+class TestNetIdManager : NetIdManager() {
+    private val nextId = AtomicInteger(MAX_NET_ID)
+    override fun reserveNetId() = nextId.decrementAndGet()
+    override fun releaseNetId(id: Int) = Unit
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e08cb16..a028a54 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -28,8 +28,6 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
-import static android.net.ConnectivityManager.TYPE_NONE;
-import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
@@ -69,6 +67,7 @@
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 
+import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
 import static com.android.testutils.ConcurrentUtilsKt.await;
 import static com.android.testutils.ConcurrentUtilsKt.durationOf;
 import static com.android.testutils.ExceptionUtils.ignoreExceptions;
@@ -86,6 +85,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.anyInt;
@@ -93,6 +93,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -105,6 +106,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.app.AlarmManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -125,6 +127,7 @@
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
 import android.net.IDnsResolver;
+import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
@@ -139,12 +142,8 @@
 import android.net.LinkProperties;
 import android.net.MatchAllNetworkSpecifier;
 import android.net.Network;
-import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
@@ -168,7 +167,6 @@
 import android.os.HandlerThread;
 import android.os.INetworkManagementService;
 import android.os.Looper;
-import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -265,10 +263,12 @@
     // timeout. For this, our assertions should run fast enough to leave less than
     // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
     // supposedly fired, and the time we call expectCallback.
-    private final static int TEST_CALLBACK_TIMEOUT_MS = 200;
+    private static final int TEST_CALLBACK_TIMEOUT_MS = 200;
     // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
     // complete before callbacks are verified.
-    private final static int TEST_REQUEST_TIMEOUT_MS = 150;
+    private static final int TEST_REQUEST_TIMEOUT_MS = 150;
+
+    private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000;
 
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
@@ -276,15 +276,19 @@
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     private MockContext mServiceContext;
-    private WrappedConnectivityService mService;
+    private HandlerThread mCsHandlerThread;
+    private ConnectivityService mService;
     private WrappedConnectivityManager mCm;
-    private MockNetworkAgent mWiFiNetworkAgent;
-    private MockNetworkAgent mCellNetworkAgent;
-    private MockNetworkAgent mEthernetNetworkAgent;
+    private TestNetworkAgentWrapper mWiFiNetworkAgent;
+    private TestNetworkAgentWrapper mCellNetworkAgent;
+    private TestNetworkAgentWrapper mEthernetNetworkAgent;
     private MockVpn mMockVpn;
     private Context mContext;
     private INetworkPolicyListener mPolicyListener;
+    private WrappedMultinetworkPolicyTracker mPolicyTracker;
+    private HandlerThread mAlarmManagerThread;
 
+    @Mock IIpConnectivityMetrics mIpConnectivityMetrics;
     @Mock IpConnectivityMetrics.Logger mMetricsService;
     @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
     @Mock INetworkManagementService mNetworkManagementService;
@@ -296,6 +300,7 @@
     @Mock PackageManager mPackageManager;
     @Mock UserManager mUserManager;
     @Mock NotificationManager mNotificationManager;
+    @Mock AlarmManager mAlarmManager;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -368,6 +373,7 @@
             if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
             if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
+            if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
             return super.getSystemService(name);
         }
 
@@ -397,25 +403,20 @@
         }
     }
 
-    public void waitForIdle(int timeoutMsAsInt) {
-        long timeoutMs = timeoutMsAsInt;
-        HandlerUtilsKt.waitForIdle(mService.mHandlerThread, timeoutMs);
-        waitForIdle(mCellNetworkAgent, timeoutMs);
-        waitForIdle(mWiFiNetworkAgent, timeoutMs);
-        waitForIdle(mEthernetNetworkAgent, timeoutMs);
-        HandlerUtilsKt.waitForIdle(mService.mHandlerThread, timeoutMs);
-        HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), timeoutMs);
+    private void waitForIdle() {
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+        waitForIdle(mCellNetworkAgent, TIMEOUT_MS);
+        waitForIdle(mWiFiNetworkAgent, TIMEOUT_MS);
+        waitForIdle(mEthernetNetworkAgent, TIMEOUT_MS);
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+        HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS);
     }
 
-    public void waitForIdle(MockNetworkAgent agent, long timeoutMs) {
+    private void waitForIdle(TestNetworkAgentWrapper agent, long timeoutMs) {
         if (agent == null) {
             return;
         }
-        HandlerUtilsKt.waitForIdle(agent.mHandlerThread, timeoutMs);
-    }
-
-    private void waitForIdle() {
-        waitForIdle(TIMEOUT_MS);
+        agent.waitForIdle(timeoutMs);
     }
 
     @Test
@@ -429,7 +430,7 @@
 
         // Bring up a network that we can use to send messages to ConnectivityService.
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
         Network n = mWiFiNetworkAgent.getNetwork();
@@ -449,7 +450,7 @@
     public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception {
         // Bring up a network that we can use to send messages to ConnectivityService.
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
         Network n = mWiFiNetworkAgent.getNetwork();
@@ -469,7 +470,7 @@
         fail("expected race condition at least once in " + attempts + " attempts");
     }
 
-    private class MockNetworkAgent implements TestableNetworkCallback.HasNetwork {
+    private class TestNetworkAgentWrapper extends NetworkAgentWrapper {
         private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS
                 | NETWORK_VALIDATION_PROBE_HTTP
                 | NETWORK_VALIDATION_PROBE_HTTPS;
@@ -480,86 +481,35 @@
                 | NETWORK_VALIDATION_RESULT_PARTIAL;
         private static final int VALIDATION_RESULT_INVALID = 0;
 
-        private final INetworkMonitor mNetworkMonitor;
-        private final NetworkInfo mNetworkInfo;
-        private final NetworkCapabilities mNetworkCapabilities;
-        private final HandlerThread mHandlerThread;
-        private final ConditionVariable mDisconnected = new ConditionVariable();
-        private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
-        private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
-        private int mScore;
-        private NetworkAgent mNetworkAgent;
-        private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
-        private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
-        private Integer mExpectedKeepaliveSlot = null;
-        // Contains the redirectUrl from networkStatus(). Before reading, wait for
-        // mNetworkStatusReceived.
-        private String mRedirectUrl;
-
+        private INetworkMonitor mNetworkMonitor;
         private INetworkMonitorCallbacks mNmCallbacks;
         private int mNmValidationResult = VALIDATION_RESULT_BASE;
         private String mNmValidationRedirectUrl = null;
         private boolean mNmProvNotificationRequested = false;
 
-        void setNetworkValid() {
-            mNmValidationResult = VALIDATION_RESULT_VALID;
-            mNmValidationRedirectUrl = null;
-        }
+        private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
+        // Contains the redirectUrl from networkStatus(). Before reading, wait for
+        // mNetworkStatusReceived.
+        private String mRedirectUrl;
 
-        void setNetworkInvalid() {
-            mNmValidationResult = VALIDATION_RESULT_INVALID;
-            mNmValidationRedirectUrl = null;
-        }
-
-        void setNetworkPortal(String redirectUrl) {
-            setNetworkInvalid();
-            mNmValidationRedirectUrl = redirectUrl;
-        }
-
-        void setNetworkPartial() {
-            mNmValidationResult = VALIDATION_RESULT_PARTIAL;
-            mNmValidationRedirectUrl = null;
-        }
-
-        void setNetworkPartialValid() {
-            mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
-            mNmValidationRedirectUrl = null;
-        }
-
-        MockNetworkAgent(int transport) throws Exception {
+        TestNetworkAgentWrapper(int transport) throws Exception {
             this(transport, new LinkProperties());
         }
 
-        MockNetworkAgent(int transport, LinkProperties linkProperties) throws Exception {
-            final int type = transportToLegacyType(transport);
-            final String typeName = ConnectivityManager.getNetworkTypeName(type);
-            mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
-            mNetworkCapabilities = new NetworkCapabilities();
-            mNetworkCapabilities.addTransportType(transport);
-            switch (transport) {
-                case TRANSPORT_ETHERNET:
-                    mScore = 70;
-                    break;
-                case TRANSPORT_WIFI:
-                    mScore = 60;
-                    break;
-                case TRANSPORT_CELLULAR:
-                    mScore = 50;
-                    break;
-                case TRANSPORT_WIFI_AWARE:
-                    mScore = 20;
-                    break;
-                case TRANSPORT_VPN:
-                    mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
-                    mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
-                    break;
-                default:
-                    throw new UnsupportedOperationException("unimplemented network type");
-            }
-            mHandlerThread = new HandlerThread("Mock-" + typeName);
-            mHandlerThread.start();
+        TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
+                throws Exception {
+            super(transport, linkProperties, mServiceContext);
 
+            // Waits for the NetworkAgent to be registered, which includes the creation of the
+            // NetworkMonitor.
+            waitForIdle(TIMEOUT_MS);
+        }
+
+        @Override
+        protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties)
+                throws Exception {
             mNetworkMonitor = mock(INetworkMonitor.class);
+
             final Answer validateAnswer = inv -> {
                 new Thread(ignoreExceptions(this::onValidationRequested)).start();
                 return null;
@@ -576,56 +526,20 @@
                     any() /* name */,
                     nmCbCaptor.capture());
 
-            mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
-                    "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
-                    linkProperties, mScore, new NetworkMisc(), NetworkFactory.SerialNumber.NONE) {
-                @Override
-                public void unwanted() { mDisconnected.open(); }
-
-                @Override
-                public void startSocketKeepalive(Message msg) {
-                    int slot = msg.arg1;
-                    if (mExpectedKeepaliveSlot != null) {
-                        assertEquals((int) mExpectedKeepaliveSlot, slot);
-                    }
-                    onSocketKeepaliveEvent(slot, mStartKeepaliveError);
-                }
-
-                @Override
-                public void stopSocketKeepalive(Message msg) {
-                    onSocketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
-                }
-
+            final InstrumentedNetworkAgent na = new InstrumentedNetworkAgent(this, linkProperties) {
                 @Override
                 public void networkStatus(int status, String redirectUrl) {
                     mRedirectUrl = redirectUrl;
                     mNetworkStatusReceived.open();
                 }
-
-                @Override
-                protected void preventAutomaticReconnect() {
-                    mPreventReconnectReceived.open();
-                }
-
-                @Override
-                protected void addKeepalivePacketFilter(Message msg) {
-                    Log.i(TAG, "Add keepalive packet filter.");
-                }
-
-                @Override
-                protected void removeKeepalivePacketFilter(Message msg) {
-                    Log.i(TAG, "Remove keepalive packet filter.");
-                }
             };
 
-            assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId);
+            assertEquals(na.netId, nmNetworkCaptor.getValue().netId);
             mNmCallbacks = nmCbCaptor.getValue();
 
             mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
 
-            // Waits for the NetworkAgent to be registered, which includes the creation of the
-            // NetworkMonitor.
-            waitForIdle();
+            return na;
         }
 
         private void onValidationRequested() throws Exception {
@@ -645,55 +559,11 @@
             }
         }
 
-        public void adjustScore(int change) {
-            mScore += change;
-            mNetworkAgent.sendNetworkScore(mScore);
-        }
-
-        public int getScore() {
-            return mScore;
-        }
-
-        public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
-            mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated);
-        }
-
-        public void addCapability(int capability) {
-            mNetworkCapabilities.addCapability(capability);
-            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
-        }
-
-        public void removeCapability(int capability) {
-            mNetworkCapabilities.removeCapability(capability);
-            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
-        }
-
-        public void setUids(Set<UidRange> uids) {
-            mNetworkCapabilities.setUids(uids);
-            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
-        }
-
-        public void setSignalStrength(int signalStrength) {
-            mNetworkCapabilities.setSignalStrength(signalStrength);
-            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
-        }
-
-        public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
-            mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
-            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
-        }
-
-        public void setNetworkCapabilities(NetworkCapabilities nc,
-                boolean sendToConnectivityService) {
-            mNetworkCapabilities.set(nc);
-            if (sendToConnectivityService) {
-                mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
-            }
-        }
-
+        /**
+         * Connect without adding any internet capability.
+         */
         public void connectWithoutInternet() {
-            mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
-            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+            super.connect();
         }
 
         /**
@@ -710,23 +580,21 @@
          * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
          */
         public void connect(boolean validated, boolean hasInternet) {
-            assertEquals("MockNetworkAgents can only be connected once",
-                    mNetworkInfo.getDetailedState(), DetailedState.IDLE);
-            assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
+            assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET));
 
-            NetworkCallback callback = null;
+            ConnectivityManager.NetworkCallback callback = null;
             final ConditionVariable validatedCv = new ConditionVariable();
             if (validated) {
                 setNetworkValid();
                 NetworkRequest request = new NetworkRequest.Builder()
-                        .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
+                        .addTransportType(getNetworkCapabilities().getTransportTypes()[0])
                         .clearCapabilities()
                         .build();
-                callback = new NetworkCallback() {
+                callback = new ConnectivityManager.NetworkCallback() {
                     public void onCapabilitiesChanged(Network network,
                             NetworkCapabilities networkCapabilities) {
                         if (network.equals(getNetwork()) &&
-                            networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                                networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
                             validatedCv.open();
                         }
                     }
@@ -763,47 +631,29 @@
             connect(false);
         }
 
-        public void suspend() {
-            mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null);
-            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        void setNetworkValid() {
+            mNmValidationResult = VALIDATION_RESULT_VALID;
+            mNmValidationRedirectUrl = null;
         }
 
-        public void resume() {
-            mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
-            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        void setNetworkInvalid() {
+            mNmValidationResult = VALIDATION_RESULT_INVALID;
+            mNmValidationRedirectUrl = null;
         }
 
-        public void disconnect() {
-            mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
-            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        void setNetworkPortal(String redirectUrl) {
+            setNetworkInvalid();
+            mNmValidationRedirectUrl = redirectUrl;
         }
 
-        public Network getNetwork() {
-            return new Network(mNetworkAgent.netId);
+        void setNetworkPartial() {
+            mNmValidationResult = VALIDATION_RESULT_PARTIAL;
+            mNmValidationRedirectUrl = null;
         }
 
-        public ConditionVariable getPreventReconnectReceived() {
-            return mPreventReconnectReceived;
-        }
-
-        public ConditionVariable getDisconnectedCV() {
-            return mDisconnected;
-        }
-
-        public void sendLinkProperties(LinkProperties lp) {
-            mNetworkAgent.sendLinkProperties(lp);
-        }
-
-        public void setStartKeepaliveError(int error) {
-            mStartKeepaliveError = error;
-        }
-
-        public void setStopKeepaliveError(int error) {
-            mStopKeepaliveError = error;
-        }
-
-        public void setExpectedKeepaliveSlot(Integer slot) {
-            mExpectedKeepaliveSlot = slot;
+        void setNetworkPartialValid() {
+            mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
+            mNmValidationRedirectUrl = null;
         }
 
         public String waitForRedirectUrl() {
@@ -811,12 +661,12 @@
             return mRedirectUrl;
         }
 
-        public NetworkAgent getNetworkAgent() {
-            return mNetworkAgent;
+        public void expectDisconnected() {
+            expectDisconnected(TIMEOUT_MS);
         }
 
-        public NetworkCapabilities getNetworkCapabilities() {
-            return mNetworkCapabilities;
+        public void expectPreventReconnectReceived() {
+            expectPreventReconnectReceived(TIMEOUT_MS);
         }
     }
 
@@ -995,15 +845,15 @@
         private boolean mConnected = false;
         // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
         // not inherit from NetworkAgent.
-        private MockNetworkAgent mMockNetworkAgent;
+        private TestNetworkAgentWrapper mMockNetworkAgent;
 
         public MockVpn(int userId) {
             super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
                     userId);
         }
 
-        public void setNetworkAgent(MockNetworkAgent agent) {
-            waitForIdle(agent, TIMEOUT_MS);
+        public void setNetworkAgent(TestNetworkAgentWrapper agent) {
+            agent.waitForIdle(TIMEOUT_MS);
             mMockNetworkAgent = agent;
             mNetworkAgent = agent.getNetworkAgent();
             mNetworkCapabilities.set(agent.getNetworkCapabilities());
@@ -1070,187 +920,45 @@
         }
     }
 
-    private class FakeWakeupMessage extends WakeupMessage {
-        private static final int UNREASONABLY_LONG_WAIT = 1000;
-
-        public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
-            super(context, handler, cmdName, cmd);
-        }
-
-        public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd,
-                int arg1, int arg2, Object obj) {
-            super(context, handler, cmdName, cmd, arg1, arg2, obj);
-        }
-
-        @Override
-        public void schedule(long when) {
-            long delayMs = when - SystemClock.elapsedRealtime();
-            if (delayMs < 0) delayMs = 0;
-            if (delayMs > UNREASONABLY_LONG_WAIT) {
-                fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT +
-                        "ms into the future: " + delayMs);
-            }
-            Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
-            mHandler.sendMessageDelayed(msg, delayMs);
-        }
-
-        @Override
-        public void cancel() {
-            mHandler.removeMessages(mCmd, mObj);
-        }
-
-        @Override
-        public void onAlarm() {
-            throw new AssertionError("Should never happen. Update this fake.");
+    private void mockVpn(int uid) {
+        synchronized (mService.mVpns) {
+            int userId = UserHandle.getUserId(uid);
+            mMockVpn = new MockVpn(userId);
+            // This has no effect unless the VPN is actually connected, because things like
+            // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
+            // netId, and check if that network is actually connected.
+            mService.mVpns.put(userId, mMockVpn);
         }
     }
 
-    private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
-        public volatile boolean configRestrictsAvoidBadWifi;
-        public volatile int configMeteredMultipathPreference;
+    private void setUidRulesChanged(int uidRules) throws RemoteException {
+        mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+    }
 
-        public WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
+    private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
+        mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+    }
+
+    private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) {
+        return mService.getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
+    }
+
+    private static class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
+        volatile boolean mConfigRestrictsAvoidBadWifi;
+        volatile int mConfigMeteredMultipathPreference;
+
+        WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
             super(c, h, r);
         }
 
         @Override
         public boolean configRestrictsAvoidBadWifi() {
-            return configRestrictsAvoidBadWifi;
+            return mConfigRestrictsAvoidBadWifi;
         }
 
         @Override
         public int configMeteredMultipathPreference() {
-            return configMeteredMultipathPreference;
-        }
-    }
-
-    private class WrappedConnectivityService extends ConnectivityService {
-        public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker;
-        private MockableSystemProperties mSystemProperties;
-
-        public WrappedConnectivityService(Context context, INetworkManagementService netManager,
-                INetworkStatsService statsService, INetworkPolicyManager policyManager,
-                IpConnectivityLog log, INetd netd, IDnsResolver dnsResolver) {
-            super(context, netManager, statsService, policyManager, dnsResolver, log, netd);
-            mNetd = netd;
-            mLingerDelayMs = TEST_LINGER_DELAY_MS;
-        }
-
-        @Override
-        protected MockableSystemProperties getSystemProperties() {
-            // Minimal approach to overriding system properties: let most calls fall through to real
-            // device values, and only override ones values that are important to this test.
-            mSystemProperties = spy(new MockableSystemProperties());
-            when(mSystemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0);
-            when(mSystemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false);
-            return mSystemProperties;
-        }
-
-        @Override
-        protected Tethering makeTethering() {
-            return mock(Tethering.class);
-        }
-
-        @Override
-        protected ProxyTracker makeProxyTracker() {
-            return mock(ProxyTracker.class);
-        }
-
-        @Override
-        protected int reserveNetId() {
-            while (true) {
-                final int netId = super.reserveNetId();
-
-                // Don't overlap test NetIDs with real NetIDs as binding sockets to real networks
-                // can have odd side-effects, like network validations succeeding.
-                Context context = InstrumentationRegistry.getContext();
-                final Network[] networks = ConnectivityManager.from(context).getAllNetworks();
-                boolean overlaps = false;
-                for (Network network : networks) {
-                    if (netId == network.netId) {
-                        overlaps = true;
-                        break;
-                    }
-                }
-                if (overlaps) continue;
-
-                return netId;
-            }
-        }
-
-        @Override
-        protected boolean queryUserAccess(int uid, int netId) {
-            return true;
-        }
-
-        public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
-            return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
-        }
-
-        @Override
-        public MultinetworkPolicyTracker createMultinetworkPolicyTracker(
-                Context c, Handler h, Runnable r) {
-            final WrappedMultinetworkPolicyTracker tracker = new WrappedMultinetworkPolicyTracker(c, h, r);
-            return tracker;
-        }
-
-        public WrappedMultinetworkPolicyTracker getMultinetworkPolicyTracker() {
-            return (WrappedMultinetworkPolicyTracker) mMultinetworkPolicyTracker;
-        }
-
-        @Override
-        protected NetworkStackClient getNetworkStack() {
-            return mNetworkStack;
-        }
-
-        @Override
-        public WakeupMessage makeWakeupMessage(
-                Context context, Handler handler, String cmdName, int cmd, Object obj) {
-            return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
-        }
-
-        @Override
-        public boolean hasService(String name) {
-            // Currenty, the only relevant service that ConnectivityService checks for is
-            // ETHERNET_SERVICE.
-            return Context.ETHERNET_SERVICE.equals(name);
-        }
-
-        @Override
-        protected IpConnectivityMetrics.Logger metricsLogger() {
-            return mMetricsService;
-        }
-
-        @Override
-        protected void registerNetdEventCallback() {
-        }
-
-        public void mockVpn(int uid) {
-            synchronized (mVpns) {
-                int userId = UserHandle.getUserId(uid);
-                mMockVpn = new MockVpn(userId);
-                // This has no effect unless the VPN is actually connected, because things like
-                // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
-                // netId, and check if that network is actually connected.
-                mVpns.put(userId, mMockVpn);
-            }
-        }
-
-        public void waitForIdle(int timeoutMs) {
-            HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs);
-        }
-
-        public void waitForIdle() {
-            waitForIdle(TIMEOUT_MS);
-        }
-
-        public void setUidRulesChanged(int uidRules) throws RemoteException {
-            mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
-        }
-
-        public void setRestrictBackgroundChanged(boolean restrictBackground)
-                throws RemoteException {
-            mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+            return mConfigMeteredMultipathPreference;
         }
     }
 
@@ -1296,13 +1004,22 @@
         LocalServices.addService(
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
 
-        mService = new WrappedConnectivityService(mServiceContext,
+        mAlarmManagerThread = new HandlerThread("TestAlarmManager");
+        mAlarmManagerThread.start();
+        initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler());
+
+        mCsHandlerThread = new HandlerThread("TestConnectivityService");
+        final ConnectivityService.Dependencies deps = makeDependencies();
+        mService = new ConnectivityService(mServiceContext,
                 mNetworkManagementService,
                 mStatsService,
                 mNpm,
+                mMockDnsResolver,
                 mock(IpConnectivityLog.class),
                 mMockNetd,
-                mMockDnsResolver);
+                deps);
+        mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+        verify(deps).makeMultinetworkPolicyTracker(any(), any(), any());
 
         final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
                 ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -1313,7 +1030,7 @@
         // getSystemService() correctly.
         mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
         mService.systemReady();
-        mService.mockVpn(Process.myUid());
+        mockVpn(Process.myUid());
         mCm.bindProcessToNetwork(null);
 
         // Ensure that the default setting for Captive Portals is used for most tests
@@ -1322,6 +1039,57 @@
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
     }
 
+    private ConnectivityService.Dependencies makeDependencies() {
+        final MockableSystemProperties systemProperties = spy(new MockableSystemProperties());
+        when(systemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0);
+        when(systemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false);
+
+        final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
+        doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
+        doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
+        doReturn(mNetworkStack).when(deps).getNetworkStack();
+        doReturn(systemProperties).when(deps).getSystemProperties();
+        doReturn(mock(Tethering.class)).when(deps).makeTethering(any(), any(), any(), any(), any());
+        doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
+        doReturn(mMetricsService).when(deps).getMetricsLogger();
+        doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
+        doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
+        doReturn(true).when(deps).hasService(Context.ETHERNET_SERVICE);
+        doAnswer(inv -> {
+            mPolicyTracker = new WrappedMultinetworkPolicyTracker(
+                    inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
+            return mPolicyTracker;
+        }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+
+        return deps;
+    }
+
+    private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
+        doAnswer(inv -> {
+            final long when = inv.getArgument(1);
+            final WakeupMessage wakeupMsg = inv.getArgument(3);
+            final Handler handler = inv.getArgument(4);
+
+            long delayMs = when - SystemClock.elapsedRealtime();
+            if (delayMs < 0) delayMs = 0;
+            if (delayMs > UNREASONABLY_LONG_ALARM_WAIT_MS) {
+                fail("Attempting to send msg more than " + UNREASONABLY_LONG_ALARM_WAIT_MS
+                        + "ms into the future: " + delayMs);
+            }
+            alarmHandler.postDelayed(() -> handler.post(wakeupMsg::onAlarm), wakeupMsg /* token */,
+                    delayMs);
+
+            return null;
+        }).when(am).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(),
+                any(WakeupMessage.class), any());
+
+        doAnswer(inv -> {
+            final WakeupMessage wakeupMsg = inv.getArgument(0);
+            alarmHandler.removeCallbacksAndMessages(wakeupMsg /* token */);
+            return null;
+        }).when(am).cancel(any(WakeupMessage.class));
+    }
+
     @After
     public void tearDown() throws Exception {
         setAlwaysOnNetworks(false);
@@ -1338,6 +1106,9 @@
             mEthernetNetworkAgent = null;
         }
         FakeSettingsProvider.clearSettingsProvider();
+
+        mCsHandlerThread.quitSafely();
+        mAlarmManagerThread.quitSafely();
     }
 
     private void mockDefaultPackages() throws Exception {
@@ -1357,21 +1128,6 @@
                 }));
     }
 
-   private static int transportToLegacyType(int transport) {
-        switch (transport) {
-            case TRANSPORT_ETHERNET:
-                return TYPE_ETHERNET;
-            case TRANSPORT_WIFI:
-                return TYPE_WIFI;
-            case TRANSPORT_CELLULAR:
-                return TYPE_MOBILE;
-            case TRANSPORT_VPN:
-                return TYPE_VPN;
-            default:
-                return TYPE_NONE;
-        }
-    }
-
     private void verifyActiveNetwork(int transport) {
         // Test getActiveNetworkInfo()
         assertNotNull(mCm.getActiveNetworkInfo());
@@ -1449,8 +1205,8 @@
     @Test
     public void testLingering() throws Exception {
         verifyNoNetwork();
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         assertNull(mCm.getActiveNetworkInfo());
         assertNull(mCm.getActiveNetwork());
         // Test bringing up validated cellular.
@@ -1474,7 +1230,7 @@
         assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
                 mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork()));
         // Test cellular linger timeout.
-        waitFor(mCellNetworkAgent.getDisconnectedCV());
+        mCellNetworkAgent.expectDisconnected();
         waitForIdle();
         assertLength(1, mCm.getAllNetworks());
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1490,13 +1246,13 @@
     @Test
     public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
         // Test bringing up unvalidated WiFi
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test bringing up unvalidated cellular
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);
         waitForIdle();
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1505,7 +1261,7 @@
         waitForIdle();
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test bringing up validated cellular
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         cv = waitForConnectivityBroadcasts(2);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
@@ -1525,13 +1281,13 @@
     @Test
     public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
         // Test bringing up unvalidated cellular.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mCellNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test bringing up unvalidated WiFi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         cv = waitForConnectivityBroadcasts(2);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
@@ -1551,7 +1307,7 @@
     @Test
     public void testUnlingeringDoesNotValidate() throws Exception {
         // Test bringing up unvalidated WiFi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
@@ -1559,7 +1315,7 @@
         assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         // Test bringing up validated cellular.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         cv = waitForConnectivityBroadcasts(2);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
@@ -1579,13 +1335,13 @@
     @Test
     public void testCellularOutscoresWeakWifi() throws Exception {
         // Test bringing up validated cellular.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test bringing up validated WiFi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         cv = waitForConnectivityBroadcasts(2);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
@@ -1606,45 +1362,41 @@
     public void testReapingNetwork() throws Exception {
         // Test bringing up WiFi without NET_CAPABILITY_INTERNET.
         // Expect it to be torn down immediately because it satisfies no requests.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        ConditionVariable cv = mWiFiNetworkAgent.getDisconnectedCV();
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connectWithoutInternet();
-        waitFor(cv);
+        mWiFiNetworkAgent.expectDisconnected();
         // Test bringing up cellular without NET_CAPABILITY_INTERNET.
         // Expect it to be torn down immediately because it satisfies no requests.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        cv = mCellNetworkAgent.getDisconnectedCV();
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mCellNetworkAgent.connectWithoutInternet();
-        waitFor(cv);
+        mCellNetworkAgent.expectDisconnected();
         // Test bringing up validated WiFi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        cv = waitForConnectivityBroadcasts(1);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        final ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test bringing up unvalidated cellular.
         // Expect it to be torn down because it could never be the highest scoring network
         // satisfying the default request even if it validated.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
-        cv = mCellNetworkAgent.getDisconnectedCV();
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);
-        waitFor(cv);
+        mCellNetworkAgent.expectDisconnected();
         verifyActiveNetwork(TRANSPORT_WIFI);
-        cv = mWiFiNetworkAgent.getDisconnectedCV();
         mWiFiNetworkAgent.disconnect();
-        waitFor(cv);
+        mWiFiNetworkAgent.expectDisconnected();
     }
 
     @Test
     public void testCellularFallback() throws Exception {
         // Test bringing up validated cellular.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test bringing up validated WiFi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         cv = waitForConnectivityBroadcasts(2);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
@@ -1676,13 +1428,13 @@
     @Test
     public void testWiFiFallback() throws Exception {
         // Test bringing up unvalidated WiFi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test bringing up validated cellular.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         cv = waitForConnectivityBroadcasts(2);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
@@ -1760,7 +1512,7 @@
 
         // Test unvalidated networks
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
@@ -1775,7 +1527,7 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         cv = waitForConnectivityBroadcasts(2);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -1799,7 +1551,7 @@
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
         // Test validated networks
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -1812,7 +1564,7 @@
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         genericNetworkCallback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
@@ -1850,9 +1602,9 @@
         TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
 
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -1891,7 +1643,7 @@
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         for (int i = 0; i < 4; i++) {
-            MockNetworkAgent oldNetwork, newNetwork;
+            TestNetworkAgentWrapper oldNetwork, newNetwork;
             if (i % 2 == 0) {
                 mWiFiNetworkAgent.adjustScore(-15);
                 oldNetwork = mWiFiNetworkAgent;
@@ -1943,7 +1695,7 @@
 
         mCm.registerNetworkCallback(request, callback);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);   // Score: 10
         callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
@@ -1952,7 +1704,7 @@
 
         // Bring up wifi with a score of 20.
         // Cell stays up because it would satisfy the default request if it validated.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);   // Score: 20
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -1968,7 +1720,7 @@
 
         // Bring up wifi with a score of 70.
         // Cell is lingered because it would not satisfy any request, even if it validated.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.adjustScore(50);
         mWiFiNetworkAgent.connect(false);   // Score: 70
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -1987,7 +1739,7 @@
 
         // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
         // it's arguably correct to linger it, since it was the default network before it validated.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         // TODO: Investigate sending validated before losing.
@@ -2008,12 +1760,12 @@
         assertEquals(null, mCm.getActiveNetwork());
 
         // If a network is lingering, and we add and remove a request from it, resume lingering.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2044,7 +1796,7 @@
         mCm.requestNetwork(cellRequest, noopCallback);
 
         // Now connect wifi, and expect it to become the default network.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -2065,7 +1817,7 @@
         TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(trackDefaultCallback);
         trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
-        mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+        mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
         mEthernetNetworkAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
         callback.expectCallback(CallbackRecord.LOSING, mWiFiNetworkAgent);
@@ -2100,8 +1852,8 @@
         TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
 
         mCellNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -2142,12 +1894,12 @@
         mCm.registerNetworkCallback(request, callback);
 
         // Bring up validated cell.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
 
         // Bring up unvalidated wifi with explicitlySelected=true.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(true, false);
         mWiFiNetworkAgent.connect(false);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2170,7 +1922,7 @@
         // Disconnect wifi, and then reconnect, again with explicitlySelected=true.
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(true, false);
         mWiFiNetworkAgent.connect(false);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2181,7 +1933,7 @@
         callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
 
         // Reconnect, again with explicitlySelected=true, but this time validate.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(true, false);
         mWiFiNetworkAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2191,7 +1943,7 @@
 
         // BUG: the network will no longer linger, even though it's validated and outscored.
         // TODO: fix this.
-        mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+        mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
         mEthernetNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
         assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -2202,7 +1954,7 @@
         // wifi immediately.
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(true, true);
         mWiFiNetworkAgent.connect(false);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2215,7 +1967,7 @@
         // Check that the network is not scored specially and that the device prefers cell data.
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(false, true);
         mWiFiNetworkAgent.connect(false);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2277,7 +2029,7 @@
         assertTrue(testFactory.getMyStartRequested());
 
         // Now bring in a higher scored network.
-        MockNetworkAgent testAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        TestNetworkAgentWrapper testAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         // Rather than create a validated network which complicates things by registering it's
         // own NetworkRequest during startup, just bump up the score to cancel out the
         // unvalidated penalty.
@@ -2371,18 +2123,17 @@
     @Test
     public void testMMSonWiFi() throws Exception {
         // Test bringing up cellular without MMS NetworkRequest gets reaped
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
-        ConditionVariable cv = mCellNetworkAgent.getDisconnectedCV();
         mCellNetworkAgent.connectWithoutInternet();
-        waitFor(cv);
+        mCellNetworkAgent.expectDisconnected();
         waitForIdle();
         assertEmpty(mCm.getAllNetworks());
         verifyNoNetwork();
 
         // Test bringing up validated WiFi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        cv = waitForConnectivityBroadcasts(1);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        final ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -2394,23 +2145,22 @@
         mCm.requestNetwork(builder.build(), networkCallback);
 
         // Test bringing up unvalidated cellular with MMS
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
         mCellNetworkAgent.connectWithoutInternet();
         networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         verifyActiveNetwork(TRANSPORT_WIFI);
 
         // Test releasing NetworkRequest disconnects cellular with MMS
-        cv = mCellNetworkAgent.getDisconnectedCV();
         mCm.unregisterNetworkCallback(networkCallback);
-        waitFor(cv);
+        mCellNetworkAgent.expectDisconnected();
         verifyActiveNetwork(TRANSPORT_WIFI);
     }
 
     @Test
     public void testMMSonCell() throws Exception {
         // Test bringing up cellular without MMS
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mCellNetworkAgent.connect(false);
         waitFor(cv);
@@ -2423,16 +2173,16 @@
         mCm.requestNetwork(builder.build(), networkCallback);
 
         // Test bringing up MMS cellular network
-        MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        TestNetworkAgentWrapper
+                mmsNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
         mmsNetworkAgent.connectWithoutInternet();
         networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
 
         // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
-        cv = mmsNetworkAgent.getDisconnectedCV();
         mCm.unregisterNetworkCallback(networkCallback);
-        waitFor(cv);
+        mmsNetworkAgent.expectDisconnected();
         verifyActiveNetwork(TRANSPORT_CELLULAR);
     }
 
@@ -2446,12 +2196,12 @@
         mCm.registerNetworkCallback(request, callback);
 
         // Bring up validated mobile data.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
 
         // Bring up wifi with partial connectivity.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connectWithPartialConnectivity();
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
@@ -2484,7 +2234,7 @@
         // Disconnect and reconnect wifi with partial connectivity again.
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connectWithPartialConnectivity();
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
@@ -2500,7 +2250,7 @@
         // If user accepted partial connectivity before, and device reconnects to that network
         // again, but now the network has full connectivity. The network shouldn't contain
         // NET_CAPABILITY_PARTIAL_CONNECTIVITY.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         // acceptUnvalidated is also used as setting for accepting partial networks.
         mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */,
                 true /* acceptUnvalidated */);
@@ -2524,7 +2274,7 @@
         // The user accepted partial connectivity and selected "don't ask again". Now the user
         // reconnects to the partial connectivity network. Switch to wifi as soon as partial
         // connectivity is detected.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */,
                 true /* acceptUnvalidated */);
         mWiFiNetworkAgent.connectWithPartialConnectivity();
@@ -2548,7 +2298,7 @@
 
         // If the user accepted partial connectivity, and the device auto-reconnects to the partial
         // connectivity network, it should contain both PARTIAL_CONNECTIVITY and VALIDATED.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(false /* explicitlySelected */,
                 true /* acceptUnvalidated */);
 
@@ -2580,7 +2330,7 @@
 
         // Bring up a network with a captive portal.
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String redirectUrl = "http://android.com/path";
         mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2629,7 +2379,7 @@
 
         // Bring up a network with a captive portal.
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String firstRedirectUrl = "http://example.com/firstPath";
         mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2642,7 +2392,7 @@
 
         // Bring up a network with a captive portal.
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String secondRedirectUrl = "http://example.com/secondPath";
         mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2680,7 +2430,7 @@
         mCm.registerNetworkCallback(validatedRequest, validatedCallback);
 
         // Bring up wifi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
@@ -2740,14 +2490,12 @@
         setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID);
         // Bring up a network with a captive portal.
         // Expect it to fail to connect and not result in any callbacks.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String firstRedirectUrl = "http://example.com/firstPath";
 
-        ConditionVariable disconnectCv = mWiFiNetworkAgent.getDisconnectedCV();
-        ConditionVariable avoidCv = mWiFiNetworkAgent.getPreventReconnectReceived();
         mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
-        waitFor(disconnectCv);
-        waitFor(avoidCv);
+        mWiFiNetworkAgent.expectDisconnected();
+        mWiFiNetworkAgent.expectPreventReconnectReceived();
 
         assertNoCallbacks(captivePortalCallback, validatedCallback);
     }
@@ -2846,7 +2594,7 @@
         LocalStringNetworkSpecifier nsFoo = new LocalStringNetworkSpecifier("foo");
         LocalStringNetworkSpecifier nsBar = new LocalStringNetworkSpecifier("bar");
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2973,7 +2721,7 @@
             public void writeToParcel(Parcel dest, int flags) {}
         }
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
 
         UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier();
@@ -3026,14 +2774,14 @@
         cellNetworkCallback.assertNoCallback();
 
         // Bring up cell and expect CALLBACK_AVAILABLE.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         // Bring up wifi and expect CALLBACK_AVAILABLE.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         cellNetworkCallback.assertNoCallback();
         defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -3046,7 +2794,7 @@
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         // Bring up cell. Expect no default network callback, since it won't be the default.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         defaultNetworkCallback.assertNoCallback();
@@ -3065,7 +2813,8 @@
         assertEquals(null, mCm.getActiveNetwork());
 
         final int uid = Process.myUid();
-        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -3090,7 +2839,7 @@
         mCm.requestNetwork(cellRequest, cellNetworkCallback);
 
         // Bring up the mobile network.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
 
         // We should get onAvailable(), onCapabilitiesChanged(), and
@@ -3160,7 +2909,7 @@
         waitForIdle();
     }
 
-    private boolean isForegroundNetwork(MockNetworkAgent network) {
+    private boolean isForegroundNetwork(TestNetworkAgentWrapper network) {
         NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
         assertNotNull(nc);
         return nc.hasCapability(NET_CAPABILITY_FOREGROUND);
@@ -3179,13 +2928,13 @@
         mCm.registerNetworkCallback(request, callback);
         mCm.registerNetworkCallback(fgRequest, fgCallback);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
 
         // When wifi connects, cell lingers.
@@ -3278,7 +3027,7 @@
             }
         });
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         // Don't request that the network validate, because otherwise connect() will block until
         // the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired,
         // and we won't actually measure anything.
@@ -3295,7 +3044,7 @@
                 onAvailableDispatchingDuration <= CONNECT_TIME_LIMIT_MS);
 
         // Give wifi a high enough score that we'll linger cell when wifi comes up.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.adjustScore(40);
         mWiFiNetworkAgent.connect(false);
 
@@ -3338,7 +3087,7 @@
         assertTrue(testFactory.getMyStartRequested());
 
         // Bring up wifi. The factory stops looking for a network.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         // Score 60 - 40 penalty for not validated yet, then 60 when it validates
         testFactory.expectAddRequestsWithScores(20, 60);
         mWiFiNetworkAgent.connect(true);
@@ -3355,7 +3104,7 @@
 
         // Bring up cell data and check that the factory stops looking.
         assertLength(1, mCm.getAllNetworks());
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         testFactory.expectAddRequestsWithScores(10, 50);  // Unvalidated, then validated
         mCellNetworkAgent.connect(true);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -3384,48 +3133,46 @@
     @Test
     public void testAvoidBadWifiSetting() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
-        final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
         final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI;
 
-        tracker.configRestrictsAvoidBadWifi = false;
+        mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
         String[] values = new String[] {null, "0", "1"};
         for (int i = 0; i < values.length; i++) {
             Settings.Global.putInt(cr, settingName, 1);
-            tracker.reevaluate();
+            mPolicyTracker.reevaluate();
             waitForIdle();
             String msg = String.format("config=false, setting=%s", values[i]);
             assertTrue(mService.avoidBadWifi());
-            assertFalse(msg, tracker.shouldNotifyWifiUnvalidated());
+            assertFalse(msg, mPolicyTracker.shouldNotifyWifiUnvalidated());
         }
 
-        tracker.configRestrictsAvoidBadWifi = true;
+        mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
 
         Settings.Global.putInt(cr, settingName, 0);
-        tracker.reevaluate();
+        mPolicyTracker.reevaluate();
         waitForIdle();
         assertFalse(mService.avoidBadWifi());
-        assertFalse(tracker.shouldNotifyWifiUnvalidated());
+        assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated());
 
         Settings.Global.putInt(cr, settingName, 1);
-        tracker.reevaluate();
+        mPolicyTracker.reevaluate();
         waitForIdle();
         assertTrue(mService.avoidBadWifi());
-        assertFalse(tracker.shouldNotifyWifiUnvalidated());
+        assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated());
 
         Settings.Global.putString(cr, settingName, null);
-        tracker.reevaluate();
+        mPolicyTracker.reevaluate();
         waitForIdle();
         assertFalse(mService.avoidBadWifi());
-        assertTrue(tracker.shouldNotifyWifiUnvalidated());
+        assertTrue(mPolicyTracker.shouldNotifyWifiUnvalidated());
     }
 
     @Test
     public void testAvoidBadWifi() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
-        final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
 
         // Pretend we're on a carrier that restricts switching away from bad wifi.
-        tracker.configRestrictsAvoidBadWifi = true;
+        mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
 
         // File a request for cell to ensure it doesn't go down.
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
@@ -3444,17 +3191,17 @@
         mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback);
 
         Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0);
-        tracker.reevaluate();
+        mPolicyTracker.reevaluate();
 
         // Bring up validated cell.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         Network cellNetwork = mCellNetworkAgent.getNetwork();
 
         // Bring up validated wifi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -3476,14 +3223,14 @@
 
         // Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect
         // that we switch back to cell.
-        tracker.configRestrictsAvoidBadWifi = false;
-        tracker.reevaluate();
+        mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
+        mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
 
         // Switch back to a restrictive carrier.
-        tracker.configRestrictsAvoidBadWifi = true;
-        tracker.reevaluate();
+        mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
+        mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
 
@@ -3498,7 +3245,7 @@
 
         // Disconnect and reconnect wifi to clear the one-time switch above.
         mWiFiNetworkAgent.disconnect();
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -3512,7 +3259,7 @@
 
         // Simulate the user selecting "switch" and checking the don't ask again checkbox.
         Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
-        tracker.reevaluate();
+        mPolicyTracker.reevaluate();
 
         // We now switch to cell.
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
@@ -3525,11 +3272,11 @@
         // Simulate the user turning the cellular fallback setting off and then on.
         // We switch to wifi and then to cell.
         Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
-        tracker.reevaluate();
+        mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
         Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
-        tracker.reevaluate();
+        mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
 
@@ -3547,14 +3294,13 @@
     @Test
     public void testMeteredMultipathPreferenceSetting() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
-        final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
         final String settingName = Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
 
         for (int config : Arrays.asList(0, 3, 2)) {
             for (String setting: Arrays.asList(null, "0", "2", "1")) {
-                tracker.configMeteredMultipathPreference = config;
+                mPolicyTracker.mConfigMeteredMultipathPreference = config;
                 Settings.Global.putString(cr, settingName, setting);
-                tracker.reevaluate();
+                mPolicyTracker.reevaluate();
                 waitForIdle();
 
                 final int expected = (setting != null) ? Integer.parseInt(setting) : config;
@@ -3575,7 +3321,7 @@
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
         mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
                 TEST_CALLBACK_TIMEOUT_MS);
@@ -3595,7 +3341,7 @@
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
         mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
                 TEST_CALLBACK_TIMEOUT_MS);
@@ -3623,7 +3369,7 @@
         networkCallback.expectCallback(CallbackRecord.UNAVAILABLE, null);
 
         // create a network satisfying request - validate that request not triggered
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         networkCallback.assertNoCallback();
     }
@@ -3646,7 +3392,7 @@
         networkCallback.assertNoCallback();
 
         // create a network satisfying request - validate that request not triggered
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         networkCallback.assertNoCallback();
     }
@@ -3876,7 +3622,7 @@
             assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()));
         }
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
@@ -3940,10 +3686,10 @@
         callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
 
         // Check that a started keepalive can be stopped.
-        mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+        mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS);
         ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectStarted();
-        mWiFiNetworkAgent.setStopKeepaliveError(PacketKeepalive.SUCCESS);
+        mWiFiNetworkAgent.setStopKeepaliveEvent(PacketKeepalive.SUCCESS);
         ka.stop();
         callback.expectStopped();
 
@@ -3961,7 +3707,7 @@
         ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectStarted();
         mWiFiNetworkAgent.disconnect();
-        waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+        mWiFiNetworkAgent.expectDisconnected();
         callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK);
 
         // ... and that stopping it after that has no adverse effects.
@@ -3972,7 +3718,7 @@
 
         // Reconnect.
         myNet = connectKeepaliveNetwork(lp);
-        mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+        mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS);
 
         // Check that keepalive slots start from 1 and increment. The first one gets slot 1.
         mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
@@ -4092,12 +3838,12 @@
         }
 
         // Check that a started keepalive can be stopped.
-        mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+        mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
         try (SocketKeepalive ka = mCm.createSocketKeepalive(
                 myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
             ka.start(validKaInterval);
             callback.expectStarted();
-            mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
+            mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS);
             ka.stop();
             callback.expectStopped();
 
@@ -4137,7 +3883,7 @@
             ka.start(validKaInterval);
             callback.expectStarted();
             mWiFiNetworkAgent.disconnect();
-            waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+            mWiFiNetworkAgent.expectDisconnected();
             callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
 
             // ... and that stopping it after that has no adverse effects.
@@ -4150,7 +3896,7 @@
 
         // Reconnect.
         myNet = connectKeepaliveNetwork(lp);
-        mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+        mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
 
         // Check that keepalive slots start from 1 and increment. The first one gets slot 1.
         mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
@@ -4187,7 +3933,7 @@
         // assertFalse(isUdpPortInUse(srcPort2));
 
         mWiFiNetworkAgent.disconnect();
-        waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+        mWiFiNetworkAgent.expectDisconnected();
         mWiFiNetworkAgent = null;
     }
 
@@ -4263,7 +4009,7 @@
         testSocketV6.close();
 
         mWiFiNetworkAgent.disconnect();
-        waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+        mWiFiNetworkAgent.expectDisconnected();
         mWiFiNetworkAgent = null;
     }
 
@@ -4279,8 +4025,8 @@
         lp.addLinkAddress(new LinkAddress(myIPv4, 25));
         lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
         Network myNet = connectKeepaliveNetwork(lp);
-        mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
-        mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
+        mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
+        mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS);
 
         TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor);
 
@@ -4316,7 +4062,7 @@
         // assertFalse(isUdpPortInUse(srcPort));
 
         mWiFiNetworkAgent.disconnect();
-        waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+        mWiFiNetworkAgent.expectDisconnected();
         mWiFiNetworkAgent = null;
     }
 
@@ -4379,9 +4125,9 @@
         TestNetworkPinner.pin(mServiceContext, wifiRequest);
         assertNull(mCm.getBoundNetworkForProcess());
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
 
         // When wi-fi connects, expect to be pinned.
@@ -4394,7 +4140,7 @@
         assertNotPinnedToWifi();
 
         // Reconnecting does not cause the pin to come back.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         assertFalse(TestNetworkPinner.awaitPin(100));
         assertNotPinnedToWifi();
@@ -4416,14 +4162,14 @@
 
         // Pinning takes effect even if the pinned network is the default when the pin is set...
         TestNetworkPinner.pin(mServiceContext, wifiRequest);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         assertTrue(TestNetworkPinner.awaitPin(100));
         assertPinnedToWifiWithWifiDefault();
 
         // ... and is maintained even when that network is no longer the default.
         cv = waitForConnectivityBroadcasts(1);
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         assertPinnedToWifiWithCellDefault();
@@ -4526,7 +4272,7 @@
         ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
 
         verifyNoNetwork();
-        MockNetworkAgent wifiAware = new MockNetworkAgent(TRANSPORT_WIFI_AWARE);
+        TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE);
         assertNull(mCm.getActiveNetworkInfo());
 
         Network[] allNetworks = mCm.getAllNetworks();
@@ -4599,7 +4345,7 @@
 
         // Verify direct routes are added when network agent is first registered in
         // ConnectivityService.
-        MockNetworkAgent networkAgent = new MockNetworkAgent(TRANSPORT_WIFI, lp);
+        TestNetworkAgentWrapper networkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
         networkAgent.connect(true);
         networkCallback.expectCallback(CallbackRecord.AVAILABLE, networkAgent);
         networkCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, networkAgent);
@@ -4631,8 +4377,8 @@
 
     @Test
     public void testStatsIfacesChanged() throws Exception {
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
 
         Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
         Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
@@ -4708,7 +4454,7 @@
         // Clear any interactions that occur as a result of CS starting up.
         reset(mMockDnsResolver);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         waitForIdle();
         verify(mMockDnsResolver, never()).setResolverConfiguration(any());
         verifyNoMoreInteractions(mMockDnsResolver);
@@ -4791,7 +4537,7 @@
                 .addTransportType(TRANSPORT_CELLULAR).build();
         mCm.requestNetwork(cellRequest, cellNetworkCallback);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         waitForIdle();
         // CS tells netd about the empty DNS config for this network.
         verify(mMockDnsResolver, never()).setResolverConfiguration(any());
@@ -4880,7 +4626,7 @@
                 .addTransportType(TRANSPORT_CELLULAR).build();
         mCm.requestNetwork(cellRequest, cellNetworkCallback);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         waitForIdle();
         LinkProperties lp = new LinkProperties();
         mCellNetworkAgent.sendLinkProperties(lp);
@@ -5015,7 +4761,7 @@
         mCm.registerDefaultNetworkCallback(defaultCallback);
         defaultCallback.assertNoCallback();
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
 
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -5025,14 +4771,16 @@
         vpnNetworkCallback.assertNoCallback();
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.setUids(ranges);
         // VPN networks do not satisfy the default request and are automatically validated
         // by NetworkMonitor
-        assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
+        assertFalse(NetworkMonitorUtils.isValidationRequired(
+                vpnNetworkAgent.getNetworkCapabilities()));
         vpnNetworkAgent.setNetworkValid();
 
         vpnNetworkAgent.connect(false);
@@ -5110,13 +4858,14 @@
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
 
         defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5140,13 +4889,14 @@
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
 
         defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5170,14 +4920,15 @@
         mCm.registerDefaultNetworkCallback(callback);
 
         // Bring up Ethernet.
-        mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+        mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
         mEthernetNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
         callback.assertNoCallback();
 
         // Bring up a VPN that has the INTERNET capability, initially unvalidated.
         final int uid = Process.myUid();
-        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5199,9 +4950,10 @@
         assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
         assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
 
-        assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
+        assertFalse(NetworkMonitorUtils.isValidationRequired(
+                vpnNetworkAgent.getNetworkCapabilities()));
         assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
-                vpnNetworkAgent.mNetworkCapabilities));
+                vpnNetworkAgent.getNetworkCapabilities()));
 
         // Pretend that the VPN network validates.
         vpnNetworkAgent.setNetworkValid();
@@ -5229,7 +4981,8 @@
         mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
         vpnNetworkCallback.assertNoCallback();
 
-        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5246,7 +4999,7 @@
         assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Connect cell and use it as an underlying network.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
 
         mService.setUnderlyingNetworksForVpn(
@@ -5257,7 +5010,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.connect(true);
 
@@ -5327,7 +5080,8 @@
         mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
         vpnNetworkCallback.assertNoCallback();
 
-        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -5345,7 +5099,7 @@
         assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Connect to Cell; Cell is the default network.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
 
         vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
@@ -5354,7 +5108,7 @@
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Connect to WiFi; WiFi is the new default.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.connect(true);
 
@@ -5382,7 +5136,7 @@
     public void testIsActiveNetworkMeteredOverWifi() throws Exception {
         // Returns true by default when no network is available.
         assertTrue(mCm.isActiveNetworkMetered());
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
@@ -5394,7 +5148,7 @@
     public void testIsActiveNetworkMeteredOverCell() throws Exception {
         // Returns true by default when no network is available.
         assertTrue(mCm.isActiveNetworkMetered());
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
         mCellNetworkAgent.connect(true);
         waitForIdle();
@@ -5406,14 +5160,15 @@
     public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() throws Exception {
         // Returns true by default when no network is available.
         assertTrue(mCm.isActiveNetworkMetered());
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
         mCellNetworkAgent.connect(true);
         waitForIdle();
         assertTrue(mCm.isActiveNetworkMetered());
 
         // Connect VPN network. By default it is using current default network (Cell).
-        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         final int uid = Process.myUid();
         ranges.add(new UidRange(uid, uid));
@@ -5429,7 +5184,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // Connect WiFi.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
@@ -5460,20 +5215,21 @@
    public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception {
         // Returns true by default when no network is available.
         assertTrue(mCm.isActiveNetworkMetered());
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
         mCellNetworkAgent.connect(true);
         waitForIdle();
         assertTrue(mCm.isActiveNetworkMetered());
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
         assertFalse(mCm.isActiveNetworkMetered());
 
         // Connect VPN network.
-        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         final int uid = Process.myUid();
         ranges.add(new UidRange(uid, uid));
@@ -5531,14 +5287,15 @@
     public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() throws Exception {
         // Returns true by default when no network is available.
         assertTrue(mCm.isActiveNetworkMetered());
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
         assertFalse(mCm.isActiveNetworkMetered());
 
         // Connect VPN network.
-        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         final int uid = Process.myUid();
         ranges.add(new UidRange(uid, uid));
@@ -5582,21 +5339,21 @@
                 .build();
         mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
 
-        mService.setUidRulesChanged(RULE_REJECT_ALL);
+        setUidRulesChanged(RULE_REJECT_ALL);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
 
         // ConnectivityService should cache it not to invoke the callback again.
-        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        setUidRulesChanged(RULE_REJECT_METERED);
         cellNetworkCallback.assertNoCallback();
 
-        mService.setUidRulesChanged(RULE_NONE);
+        setUidRulesChanged(RULE_NONE);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
 
-        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        setUidRulesChanged(RULE_REJECT_METERED);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
 
         // Restrict the network based on UID rule and NOT_METERED capability change.
@@ -5607,18 +5364,18 @@
         cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
                 mCellNetworkAgent);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
-        mService.setUidRulesChanged(RULE_ALLOW_METERED);
+        setUidRulesChanged(RULE_ALLOW_METERED);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
 
-        mService.setUidRulesChanged(RULE_NONE);
+        setUidRulesChanged(RULE_NONE);
         cellNetworkCallback.assertNoCallback();
 
         // Restrict the network based on BackgroundRestricted.
-        mService.setRestrictBackgroundChanged(true);
+        setRestrictBackgroundChanged(true);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
-        mService.setRestrictBackgroundChanged(true);
+        setRestrictBackgroundChanged(true);
         cellNetworkCallback.assertNoCallback();
-        mService.setRestrictBackgroundChanged(false);
+        setRestrictBackgroundChanged(false);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
 
@@ -5631,18 +5388,18 @@
         mCm.registerDefaultNetworkCallback(defaultCallback);
 
         // No Networkcallbacks invoked before any network is active.
-        mService.setUidRulesChanged(RULE_REJECT_ALL);
-        mService.setUidRulesChanged(RULE_NONE);
-        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        setUidRulesChanged(RULE_REJECT_ALL);
+        setUidRulesChanged(RULE_NONE);
+        setUidRulesChanged(RULE_REJECT_METERED);
         defaultCallback.assertNoCallback();
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
         defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
 
         // Allow to use the network after switching to NOT_METERED network.
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         mWiFiNetworkAgent.connect(true);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -5658,8 +5415,8 @@
         defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
 
         // Verify there's no Networkcallbacks invoked after data saver on/off.
-        mService.setRestrictBackgroundChanged(true);
-        mService.setRestrictBackgroundChanged(false);
+        setRestrictBackgroundChanged(true);
+        setRestrictBackgroundChanged(false);
         defaultCallback.assertNoCallback();
 
         mCellNetworkAgent.disconnect();
@@ -5709,7 +5466,7 @@
         mCm.registerNetworkCallback(networkRequest, networkCallback);
 
         // Prepare ipv6 only link properties.
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         final int cellNetId = mCellNetworkAgent.getNetwork().netId;
         final LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -5756,7 +5513,7 @@
         verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
 
         // When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
-        Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+        Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent);
         assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix());
         mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
                 kNat64PrefixString, 96);
@@ -5863,7 +5620,7 @@
                 .build();
         mCm.registerNetworkCallback(networkRequest, networkCallback);
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         final LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
         mCellNetworkAgent.sendLinkProperties(cellLp);
@@ -5873,7 +5630,7 @@
         verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
                 eq(ConnectivityManager.TYPE_MOBILE));
 
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName(WIFI_IFNAME);
         mWiFiNetworkAgent.sendLinkProperties(wifiLp);
@@ -5898,7 +5655,7 @@
                 eq(ConnectivityManager.TYPE_MOBILE));
 
         // reconnect wifi
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         wifiLp.setInterfaceName(WIFI_IFNAME);
         mWiFiNetworkAgent.sendLinkProperties(wifiLp);
         mWiFiNetworkAgent.connect(true);
@@ -5944,7 +5701,7 @@
     public void testTcpBufferReset() throws Exception {
         final String testTcpBufferSizes = "1,2,3,4,5,6";
 
-        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         reset(mMockNetd);
         // Switching default network updates TCP buffer sizes.
         mCellNetworkAgent.connect(false);
@@ -5960,7 +5717,7 @@
     @Test
     public void testGetGlobalProxyForNetwork() throws Exception {
         final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
         when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo);
         assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork));
@@ -5969,7 +5726,7 @@
     @Test
     public void testGetProxyForActiveNetwork() throws Exception {
         final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
         assertNull(mService.getProxyForNetwork(null));
@@ -5988,14 +5745,15 @@
         final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
 
         // Set up a WiFi network with no proxy
-        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
         assertNull(mService.getProxyForNetwork(null));
 
         // Set up a VPN network with a proxy
         final int uid = Process.myUid();
-        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
         final ArraySet<UidRange> ranges = new ArraySet<>();
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setUids(ranges);
@@ -6044,7 +5802,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
 
         // Connected VPN should have interface rules set up. There are two expected invocations,
         // one during VPN uid update, one during VPN LinkProperties update
@@ -6070,7 +5828,8 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange);
+        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
+                lp, Process.SYSTEM_UID, vpnRange);
 
         // Legacy VPN should not have interface rules set up
         verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6085,7 +5844,8 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange);
+        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
+                lp, Process.SYSTEM_UID, vpnRange);
 
         // IPv6 unreachable route should not be misinterpreted as a default route
         verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6098,7 +5858,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
-        final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
 
         // Connected VPN should have interface rules set up. There are two expected invocations,
         // one during VPN uid update, one during VPN LinkProperties update
@@ -6147,7 +5907,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final UidRange vpnRange = UidRange.createForUser(VPN_USER);
-        final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID,
+        final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID,
                 Collections.singleton(vpnRange));
 
         reset(mMockNetd);
@@ -6169,9 +5929,10 @@
     }
 
 
-    private MockNetworkAgent establishVpn(LinkProperties lp, int establishingUid,
+    private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid,
             Set<UidRange> vpnRange) throws Exception {
-        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN, lp);
+        final TestNetworkAgentWrapper
+                vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp);
         vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid);
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.connect();
diff --git a/tools/dump-coverage/README.md b/tools/dump-coverage/README.md
index 2bab4bc..d1c10bc 100644
--- a/tools/dump-coverage/README.md
+++ b/tools/dump-coverage/README.md
@@ -16,7 +16,7 @@
 Then we can run the command to dump the data:
 
 ```
-adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use'
+adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use/coverage-file.ec'
 ```
 
 We can also reset the coverage information with
@@ -28,10 +28,10 @@
 then perform more actions, then dump the data again. To get the files, we can get
 
 ```
-adb pull /data/data/com.android.deskclock/folder-to-use ~/path-on-your-computer
+adb pull /data/data/com.android.deskclock/folder-to-use/coverage-file.ec ~/path-on-your-computer
 ```
 
-And you should have timestamped `.exec` files on your machine under the folder `~/path-on-your-computer`
+And you should have `coverage-file.ec` on your machine under the folder `~/path-on-your-computer`
 
 # Details
 
diff --git a/tools/dump-coverage/dump_coverage.cc b/tools/dump-coverage/dump_coverage.cc
index 3de1865..0808e77 100644
--- a/tools/dump-coverage/dump_coverage.cc
+++ b/tools/dump-coverage/dump_coverage.cc
@@ -18,20 +18,10 @@
 #include <jvmti.h>
 #include <string.h>
 
-#include <atomic>
-#include <ctime>
 #include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <istream>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
 
 using std::get;
 using std::tuple;
-using std::chrono::system_clock;
 
 namespace dump_coverage {
 
@@ -87,35 +77,11 @@
   return java_result_array;
 }
 
-// Gets the filename to write execution data to
-//  dirname: the directory in which to place the file
-//  outputs <dirname>/YYYY-MM-DD-HH-MM-SS.SSS.exec
-static std::string GetFilename(const std::string& dirname) {
-  system_clock::time_point time_point = system_clock::now();
-  auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(time_point);
-  auto fractional_time = time_point - seconds;
-  auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(fractional_time);
-
-  std::time_t time = system_clock::to_time_t(time_point);
-  auto tm = *std::gmtime(&time);
-
-  std::ostringstream oss;
-  oss
-    << dirname
-    << "/"
-    << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S.")
-    << std::setfill('0') << std::setw(3) << millis.count()
-    << ".ec";
-  return oss.str();
-}
-
-// Writes the execution data to a file
-//  data, length: represent the data, as a sequence of bytes
-//  dirname: directory name to contain the file
+// Writes the execution data to a file.
+//  data, length: represent the data, as a sequence of bytes.
+//  filename: file to write coverage data to.
 //  returns JNI_ERR if there is an error in writing the file, otherwise JNI_OK.
-static jint WriteFile(const char* data, int length, const std::string& dirname) {
-  auto filename = GetFilename(dirname);
-
+static jint WriteFile(const char* data, int length, const std::string& filename) {
   LOG(INFO) << "Writing file of length " << length << " to '" << filename
             << "'";
   std::ofstream file(filename, std::ios::binary);
@@ -136,11 +102,11 @@
   return JNI_OK;
 }
 
-// Grabs execution data and writes it to a file
-//  dirname: directory name to contain the file
+// Grabs execution data and writes it to a file.
+//  filename: file to write coverage data to.
 //  returns JNI_ERR if there is an error writing the file.
 // Will crash if the Agent isn't found or if any Java Exception occurs.
-static jint Dump(const std::string& dirname) {
+static jint Dump(const std::string& filename) {
   LOG(INFO) << "Dumping file";
 
   JNIEnv* env = GetJNIEnv();
@@ -152,12 +118,12 @@
 
   int result_len = env->GetArrayLength(java_result_array);
 
-  return WriteFile((const char*) result_ptr, result_len, dirname);
+  return WriteFile((const char*) result_ptr, result_len, filename);
 }
 
 // Resets execution data, performing the equivalent of
 //  Agent.getInstance().reset();
-//  args: should be empty
+//  args: should be empty.
 //  returns JNI_ERR if the arguments are invalid.
 // Will crash if the Agent isn't found or if any Java Exception occurs.
 static jint Reset(const std::string& args) {
diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp
index 2488341..87b31d2 100644
--- a/tools/preload-check/Android.bp
+++ b/tools/preload-check/Android.bp
@@ -19,4 +19,5 @@
     libs: ["tradefed"],
     test_suites: ["general-tests"],
     required: ["preload-check-device"],
+    data: [":preload-check-device"],
 }
diff --git a/tools/preload-check/device/Android.bp b/tools/preload-check/device/Android.bp
index 7782b0d..f40d8ba 100644
--- a/tools/preload-check/device/Android.bp
+++ b/tools/preload-check/device/Android.bp
@@ -20,7 +20,6 @@
 
     sdk_version: "current",
     srcs: ["src/**/*.java"],
-    test_suites: ["general-tests"],
     dex_preopt: {
         enabled: false,
     },
diff --git a/tools/preload2/Android.bp b/tools/preload2/Android.bp
deleted file mode 100644
index 5809421..0000000
--- a/tools/preload2/Android.bp
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 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 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.
-
-java_library_host {
-    name: "preload2",
-
-    srcs: ["src/**/*.java"],
-
-    // To connect to devices (and take hprof dumps).
-    static_libs: [
-        "ddmlib-prebuilt",
-        "tools-common-prebuilt",
-
-        // To process hprof dumps.
-        "perflib-prebuilt",
-
-        "trove-prebuilt",
-        "guavalib",
-
-        // For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
-        // convenience (and to not depend on internal JDK APIs).
-        "apache-harmony-jdwp-tests",
-        "junit",
-    ],
-
-    // Copy to build artifacts
-    dist: {
-        targets: [
-            "dist_files",
-        ],
-    },
-}
-
-// Copy the preload-tool shell script to the host's bin directory.
-sh_binary_host {
-    name: "preload-tool",
-    src: "preload-tool",
-    required: ["preload2"],
-}
diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool
deleted file mode 100644
index 322b62f..0000000
--- a/tools/preload2/preload-tool
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 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 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.
-
-# This script is used on the host only. It uses a common subset
-# shell dialect that should work well. It is partially derived
-# from art/tools/art.
-
-function follow_links() {
-  if [ z"$BASH_SOURCE" != z ]; then
-    file="$BASH_SOURCE"
-  else
-    file="$0"
-  fi
-  while [ -h "$file" ]; do
-    # On Mac OS, readlink -f doesn't work.
-    file="$(readlink "$file")"
-  done
-  echo "$file"
-}
-
-
-PROG_NAME="$(follow_links)"
-PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-ANDROID_ROOT=$PROG_DIR/..
-
-java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@
diff --git a/tools/preload2/src/com/android/preload/ClientUtils.java b/tools/preload2/src/com/android/preload/ClientUtils.java
deleted file mode 100644
index 71ef025..0000000
--- a/tools/preload2/src/com/android/preload/ClientUtils.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 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 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.preload;
-
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-
-/**
- * Helper class for common communication with a Client (the ddms name for a running application).
- *
- * Instances take a default timeout parameter that's applied to all functions without explicit
- * timeout. Timeouts are in milliseconds.
- */
-public class ClientUtils {
-
-    private int defaultTimeout;
-
-    public ClientUtils() {
-        this(10000);
-    }
-
-    public ClientUtils(int defaultTimeout) {
-        this.defaultTimeout = defaultTimeout;
-    }
-
-    /**
-     * Shortcut for findClient with default timeout.
-     */
-    public Client findClient(IDevice device, String processName, int processPid) {
-        return findClient(device, processName, processPid, defaultTimeout);
-    }
-
-    /**
-     * Find the client with the given process name or process id. The name takes precedence over
-     * the process id (if valid). Stop looking after the given timeout.
-     *
-     * @param device The device to communicate with.
-     * @param processName The name of the process. May be null.
-     * @param processPid The pid of the process. Values less than or equal to zero are ignored.
-     * @param timeout The amount of milliseconds to wait, at most.
-     * @return The client, if found. Otherwise null.
-     */
-    public Client findClient(IDevice device, String processName, int processPid, int timeout) {
-        WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
-        return wfc.get();
-    }
-
-    /**
-     * Shortcut for findAllClients with default timeout.
-     */
-    public Client[] findAllClients(IDevice device) {
-        return findAllClients(device, defaultTimeout);
-    }
-
-    /**
-     * Retrieve all clients known to the given device. Wait at most the given timeout.
-     *
-     * @param device The device to investigate.
-     * @param timeout The amount of milliseconds to wait, at most.
-     * @return An array of clients running on the given device. May be null depending on the
-     *         device implementation.
-     */
-    public Client[] findAllClients(IDevice device, int timeout) {
-        if (device.hasClients()) {
-            return device.getClients();
-        }
-        WaitForClients wfc = new WaitForClients(device, timeout);
-        return wfc.get();
-    }
-
-    private static class WaitForClient implements IClientChangeListener {
-
-        private IDevice device;
-        private String processName;
-        private int processPid;
-        private long timeout;
-        private Client result;
-
-        public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
-            this.device = device;
-            this.processName = processName;
-            this.processPid = processPid;
-            this.timeout = timeout;
-            this.result = null;
-        }
-
-        public Client get() {
-            synchronized (this) {
-                AndroidDebugBridge.addClientChangeListener(this);
-
-                // Maybe it's already there.
-                if (result == null) {
-                    result = searchForClient(device);
-                }
-
-                if (result == null) {
-                    try {
-                        wait(timeout);
-                    } catch (InterruptedException e) {
-                        // Note: doesn't guard for spurious wakeup.
-                    }
-                }
-            }
-
-            AndroidDebugBridge.removeClientChangeListener(this);
-            return result;
-        }
-
-        private Client searchForClient(IDevice device) {
-            if (processName != null) {
-                Client tmp = device.getClient(processName);
-                if (tmp != null) {
-                    return tmp;
-                }
-            }
-            if (processPid > 0) {
-                String name = device.getClientName(processPid);
-                if (name != null && !name.isEmpty()) {
-                    Client tmp = device.getClient(name);
-                    if (tmp != null) {
-                        return tmp;
-                    }
-                }
-            }
-            if (processPid > 0) {
-                // Try manual search.
-                for (Client cl : device.getClients()) {
-                    if (cl.getClientData().getPid() == processPid
-                            && cl.getClientData().getClientDescription() != null) {
-                        return cl;
-                    }
-                }
-            }
-            return null;
-        }
-
-        private boolean isTargetClient(Client c) {
-            if (processPid > 0 && c.getClientData().getPid() == processPid) {
-                return true;
-            }
-            if (processName != null
-                    && processName.equals(c.getClientData().getClientDescription())) {
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public void clientChanged(Client arg0, int arg1) {
-            synchronized (this) {
-                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
-                    if (isTargetClient(arg0)) {
-                        result = arg0;
-                        notifyAll();
-                    }
-                }
-            }
-        }
-    }
-
-    private static class WaitForClients implements IClientChangeListener {
-
-        private IDevice device;
-        private long timeout;
-
-        public WaitForClients(IDevice device, long timeout) {
-            this.device = device;
-            this.timeout = timeout;
-        }
-
-        public Client[] get() {
-            synchronized (this) {
-                AndroidDebugBridge.addClientChangeListener(this);
-
-                if (device.hasClients()) {
-                    return device.getClients();
-                }
-
-                try {
-                    wait(timeout); // Note: doesn't guard for spurious wakeup.
-                } catch (InterruptedException exc) {
-                }
-
-                // We will be woken up when the first client data arrives. Sleep a little longer
-                // to give (hopefully all of) the rest of the clients a chance to become available.
-                // Note: a loop with timeout is brittle as well and complicated, just accept this
-                //       for now.
-                try {
-                    Thread.sleep(500);
-                } catch (InterruptedException exc) {
-                }
-            }
-
-            AndroidDebugBridge.removeClientChangeListener(this);
-
-            return device.getClients();
-        }
-
-        @Override
-        public void clientChanged(Client arg0, int arg1) {
-            synchronized (this) {
-                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
-                    notifyAll();
-                }
-            }
-        }
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/DeviceUtils.java b/tools/preload2/src/com/android/preload/DeviceUtils.java
deleted file mode 100644
index 18cab7b..0000000
--- a/tools/preload2/src/com/android/preload/DeviceUtils.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright (C) 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 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.preload;
-
-import com.android.ddmlib.AdbCommandRejectedException;
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
-import com.android.preload.classdataretrieval.hprof.Hprof;
-import com.android.ddmlib.DdmPreferences;
-import com.android.ddmlib.IDevice;
-import com.android.ddmlib.IShellOutputReceiver;
-import com.android.ddmlib.SyncException;
-import com.android.ddmlib.TimeoutException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Date;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper class for some device routines.
- */
-public class DeviceUtils {
-
-  // Locations
-  private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes";
-  // Shell commands
-  private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE;
-  private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art";
-  private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE;
-  private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE;
-  private static final String START_SHELL_CMD = "start";
-  private static final String STOP_SHELL_CMD = "stop";
-  private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system";
-  private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\"";
-
-  public static void init(int debugPort) {
-    DdmPreferences.setSelectedDebugPort(debugPort);
-
-    Hprof.init();
-
-    AndroidDebugBridge.init(true);
-
-    AndroidDebugBridge.createBridge();
-  }
-
-  /**
-   * Run a command in the shell on the device.
-   */
-  public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) {
-    doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit);
-  }
-
-  /**
-   * Run a command in the shell on the device. Collects and returns the console output.
-   */
-  public static String doShellReturnString(IDevice device, String cmdline, long timeout,
-      TimeUnit unit) {
-    CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver();
-    doShell(device, cmdline, rec, timeout, unit);
-    return rec.toString();
-  }
-
-  /**
-   * Run a command in the shell on the device, directing all output to the given receiver.
-   */
-  public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver,
-      long timeout, TimeUnit unit) {
-    try {
-      device.executeShellCommand(cmdline, receiver, timeout, unit);
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-  }
-
-  /**
-   * Run am start on the device.
-   */
-  public static void doAMStart(IDevice device, String name, String activity) {
-    doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS);
-  }
-
-  /**
-   * Find the device with the given serial. Give up after the given timeout (in milliseconds).
-   */
-  public static IDevice findDevice(String serial, int timeout) {
-    WaitForDevice wfd = new WaitForDevice(serial, timeout);
-    return wfd.get();
-  }
-
-  /**
-   * Get all devices ddms knows about. Wait at most for the given timeout.
-   */
-  public static IDevice[] findDevices(int timeout) {
-    WaitForDevice wfd = new WaitForDevice(null, timeout);
-    wfd.get();
-    return AndroidDebugBridge.getBridge().getDevices();
-  }
-
-  /**
-   * Return the build type of the given device. This is the value of the "ro.build.type"
-   * system property.
-   */
-  public static String getBuildType(IDevice device) {
-    try {
-      Future<String> buildType = device.getSystemProperty("ro.build.type");
-      return buildType.get(500, TimeUnit.MILLISECONDS);
-    } catch (Exception e) {
-    }
-    return null;
-  }
-
-  /**
-   * Check whether the given device has a pre-optimized boot image. More precisely, checks
-   * whether /system/framework/ * /boot.art exists.
-   */
-  public static boolean hasPrebuiltBootImage(IDevice device) {
-    String ret =
-        doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS);
-
-    return !ret.contains("No such file or directory");
-  }
-
-    /**
-     * Write over the preloaded-classes file with an empty or existing file and regenerate the boot
-     * image as necessary.
-     *
-     * @param device
-     * @param pcFile
-     * @param bootTimeout
-     * @throws AdbCommandRejectedException
-     * @throws IOException
-     * @throws TimeoutException
-     * @throws SyncException
-     * @return true if successfully overwritten, false otherwise
-     */
-    public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout)
-            throws AdbCommandRejectedException, IOException, TimeoutException, SyncException {
-        boolean writeEmpty = (pcFile == null);
-        if (writeEmpty) {
-            // Check if the preloaded-classes file is already empty.
-            String oldContent =
-                    doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS);
-            if (oldContent.trim().equals("")) {
-                System.out.println("Preloaded-classes already empty.");
-                return true;
-            }
-        }
-
-        // Stop the system server etc.
-        doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS);
-        // Remount the read-only system partition
-        doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS);
-        // Delete the preloaded-classes file
-        doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS);
-        // Delete the dalvik cache files
-        doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS);
-        if (writeEmpty) {
-            // Write an empty preloaded-classes file
-            doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS);
-        } else {
-            // Push the new preloaded-classes file
-            device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE);
-        }
-        // Manually reset the boot complete flag
-        doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS);
-        // Restart system server on the device
-        doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS);
-        // Wait for the boot complete flag and return the outcome.
-        return waitForBootComplete(device, bootTimeout);
-  }
-
-  private static boolean waitForBootComplete(IDevice device, long timeout) {
-    // Do a loop checking each second whether bootcomplete. Wait for at most the given
-    // threshold.
-    Date startDate = new Date();
-    for (;;) {
-      try {
-        Thread.sleep(1000);
-      } catch (InterruptedException e) {
-        // Ignore spurious wakeup.
-      }
-      // Check whether bootcomplete.
-      String ret =
-          doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS);
-      if (ret.trim().equals("1")) {
-        break;
-      }
-      System.out.println("Still not booted: " + ret);
-
-      // Check whether we timed out. This is a simplistic check that doesn't take into account
-      // things like switches in time.
-      Date endDate = new Date();
-      long seconds =
-          TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
-      if (seconds > timeout) {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  /**
-   * Enable method-tracing on device. The system should be restarted after this.
-   */
-  public static void enableTracing(IDevice device) {
-    // Disable selinux.
-    doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS);
-
-    // Make the profile directory world-writable.
-    doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS);
-
-    // Enable streaming method tracing with a small 1K buffer.
-    doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS);
-    doShell(device, "setprop dalvik.vm.method-trace-file "
-                    + "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS);
-    doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS);
-    doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS);
-  }
-
-  private static class NullShellOutputReceiver implements IShellOutputReceiver {
-    @Override
-    public boolean isCancelled() {
-      return false;
-    }
-
-    @Override
-    public void flush() {}
-
-    @Override
-    public void addOutput(byte[] arg0, int arg1, int arg2) {}
-  }
-
-  private static class CollectStringShellOutputReceiver implements IShellOutputReceiver {
-
-    private StringBuilder builder = new StringBuilder();
-
-    @Override
-    public String toString() {
-      String ret = builder.toString();
-      // Strip trailing newlines. They are especially ugly because adb uses DOS line endings.
-      while (ret.endsWith("\r") || ret.endsWith("\n")) {
-        ret = ret.substring(0, ret.length() - 1);
-      }
-      return ret;
-    }
-
-    @Override
-    public void addOutput(byte[] arg0, int arg1, int arg2) {
-      builder.append(new String(arg0, arg1, arg2));
-    }
-
-    @Override
-    public void flush() {}
-
-    @Override
-    public boolean isCancelled() {
-      return false;
-    }
-  }
-
-  private static class WaitForDevice {
-
-    private String serial;
-    private long timeout;
-    private IDevice device;
-
-    public WaitForDevice(String serial, long timeout) {
-      this.serial = serial;
-      this.timeout = timeout;
-      device = null;
-    }
-
-    public IDevice get() {
-      if (device == null) {
-          WaitForDeviceListener wfdl = new WaitForDeviceListener(serial);
-          synchronized (wfdl) {
-              AndroidDebugBridge.addDeviceChangeListener(wfdl);
-
-              // Check whether we already know about this device.
-              IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
-              if (serial != null) {
-                  for (IDevice d : devices) {
-                      if (serial.equals(d.getSerialNumber())) {
-                          // Only accept if there are clients already. Else wait for the callback informing
-                          // us that we now have clients.
-                          if (d.hasClients()) {
-                              device = d;
-                          }
-
-                          break;
-                      }
-                  }
-              } else {
-                  if (devices.length > 0) {
-                      device = devices[0];
-                  }
-              }
-
-              if (device == null) {
-                  try {
-                      wfdl.wait(timeout);
-                  } catch (InterruptedException e) {
-                      // Ignore spurious wakeups.
-                  }
-                  device = wfdl.getDevice();
-              }
-
-              AndroidDebugBridge.removeDeviceChangeListener(wfdl);
-          }
-      }
-
-      if (device != null) {
-          // Wait for clients.
-          WaitForClientsListener wfcl = new WaitForClientsListener(device);
-          synchronized (wfcl) {
-              AndroidDebugBridge.addDeviceChangeListener(wfcl);
-
-              if (!device.hasClients()) {
-                  try {
-                      wfcl.wait(timeout);
-                  } catch (InterruptedException e) {
-                      // Ignore spurious wakeups.
-                  }
-              }
-
-              AndroidDebugBridge.removeDeviceChangeListener(wfcl);
-          }
-      }
-
-      return device;
-    }
-
-    private static class WaitForDeviceListener implements IDeviceChangeListener {
-
-        private String serial;
-        private IDevice device;
-
-        public WaitForDeviceListener(String serial) {
-            this.serial = serial;
-        }
-
-        public IDevice getDevice() {
-            return device;
-        }
-
-        @Override
-        public void deviceChanged(IDevice arg0, int arg1) {
-            // We may get a device changed instead of connected. Handle like a connection.
-            deviceConnected(arg0);
-        }
-
-        @Override
-        public void deviceConnected(IDevice arg0) {
-            if (device != null) {
-                // Ignore updates.
-                return;
-            }
-
-            if (serial == null || serial.equals(arg0.getSerialNumber())) {
-                device = arg0;
-                synchronized (this) {
-                    notifyAll();
-                }
-            }
-        }
-
-        @Override
-        public void deviceDisconnected(IDevice arg0) {
-            // Ignore disconnects.
-        }
-
-    }
-
-    private static class WaitForClientsListener implements IDeviceChangeListener {
-
-        private IDevice myDevice;
-
-        public WaitForClientsListener(IDevice myDevice) {
-            this.myDevice = myDevice;
-        }
-
-        @Override
-        public void deviceChanged(IDevice arg0, int arg1) {
-            if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) {
-                // Got a client list, done here.
-                synchronized (this) {
-                    notifyAll();
-                }
-            }
-        }
-
-        @Override
-        public void deviceConnected(IDevice arg0) {
-        }
-
-        @Override
-        public void deviceDisconnected(IDevice arg0) {
-        }
-
-    }
-  }
-
-}
diff --git a/tools/preload2/src/com/android/preload/DumpData.java b/tools/preload2/src/com/android/preload/DumpData.java
deleted file mode 100644
index d997224..0000000
--- a/tools/preload2/src/com/android/preload/DumpData.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 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 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.preload;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Holds the collected data for a process.
- */
-public class DumpData {
-    /**
-     * Name of the package (=application).
-     */
-    String packageName;
-
-    /**
-     * A map of class name to a string for the classloader. This may be a toString equivalent,
-     * or just a unique ID.
-     */
-    Map<String, String> dumpData;
-
-    /**
-     * The Date when this data was captured. Mostly for display purposes.
-     */
-    Date date;
-
-    /**
-     * A cached value for the number of boot classpath classes (classloader value in dumpData is
-     * null).
-     */
-    int bcpClasses;
-
-    public DumpData(String packageName, Map<String, String> dumpData, Date date) {
-        this.packageName = packageName;
-        this.dumpData = dumpData;
-        this.date = date;
-
-        countBootClassPath();
-    }
-
-    public String getPackageName() {
-        return packageName;
-    }
-
-    public Date getDate() {
-        return date;
-    }
-
-    public Map<String, String> getDumpData() {
-        return dumpData;
-    }
-
-    public void countBootClassPath() {
-        bcpClasses = 0;
-        for (Map.Entry<String, String> e : dumpData.entrySet()) {
-            if (e.getValue() == null) {
-                bcpClasses++;
-            }
-        }
-    }
-
-    // Return an inverted mapping.
-    public Map<String, Set<String>> invertData() {
-        Map<String, Set<String>> ret = new HashMap<>();
-        for (Map.Entry<String, String> e : dumpData.entrySet()) {
-            if (!ret.containsKey(e.getValue())) {
-                ret.put(e.getValue(), new HashSet<String>());
-            }
-            ret.get(e.getValue()).add(e.getKey());
-        }
-        return ret;
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/DumpDataIO.java b/tools/preload2/src/com/android/preload/DumpDataIO.java
deleted file mode 100644
index 28625c5..0000000
--- a/tools/preload2/src/com/android/preload/DumpDataIO.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 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 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.preload;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-
-import java.io.File;
-import java.io.FileReader;
-import java.text.DateFormat;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Helper class for serialization and deserialization of a collection of DumpData objects to XML.
- */
-public class DumpDataIO {
-
-  /**
-   * Serialize the given collection to an XML document. Returns the produced string.
-   */
-  public static String serialize(Collection<DumpData> data) {
-      // We'll do this by hand, constructing a DOM or similar is too complicated for our simple
-      // use case.
-
-      StringBuilder sb = new StringBuilder();
-      sb.append("<preloaded-classes-data>\n");
-
-      for (DumpData d : data) {
-          serialize(d, sb);
-      }
-
-      sb.append("</preloaded-classes-data>\n");
-      return sb.toString();
-  }
-
-  private static void serialize(DumpData d, StringBuilder sb) {
-      sb.append("<data package=\"" + d.packageName + "\" date=\"" +
-              DateFormat.getDateTimeInstance().format(d.date) +"\">\n");
-
-      for (Map.Entry<String, String> e : d.dumpData.entrySet()) {
-          sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n");
-      }
-
-      sb.append("</data>\n");
-  }
-
-  /**
-   * Load a collection of DumpData objects from the given file.
-   */
-  public static Collection<DumpData> deserialize(File f) throws Exception {
-      // Use SAX parsing. Our format is very simple. Don't do any schema validation or such.
-
-      SAXParserFactory spf = SAXParserFactory.newInstance();
-      spf.setNamespaceAware(false);
-      SAXParser saxParser = spf.newSAXParser();
-
-      XMLReader xmlReader = saxParser.getXMLReader();
-      DumpDataContentHandler ddch = new DumpDataContentHandler();
-      xmlReader.setContentHandler(ddch);
-      xmlReader.parse(new InputSource(new FileReader(f)));
-
-      return ddch.data;
-  }
-
-  private static class DumpDataContentHandler extends DefaultHandler {
-      Collection<DumpData> data = new LinkedList<DumpData>();
-      DumpData openData = null;
-
-      @Override
-      public void startElement(String uri, String localName, String qName, Attributes attributes)
-              throws SAXException {
-          if (qName.equals("data")) {
-              if (openData != null) {
-                  throw new IllegalStateException();
-              }
-              String pkg = attributes.getValue("package");
-              String dateString = attributes.getValue("date");
-
-              if (pkg == null || dateString == null) {
-                  throw new IllegalArgumentException();
-              }
-
-              try {
-                  Date date = DateFormat.getDateTimeInstance().parse(dateString);
-                  openData = new DumpData(pkg, new HashMap<String, String>(), date);
-              } catch (Exception e) {
-                  throw new RuntimeException(e);
-              }
-          } else if (qName.equals("class")) {
-              if (openData == null) {
-                  throw new IllegalStateException();
-              }
-              String className = attributes.getValue("name");
-              String classLoader = attributes.getValue("classloader");
-
-              if (className == null || classLoader == null) {
-                  throw new IllegalArgumentException();
-              }
-
-              openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader);
-          }
-      }
-
-      @Override
-      public void endElement(String uri, String localName, String qName) throws SAXException {
-          if (qName.equals("data")) {
-              if (openData == null) {
-                  throw new IllegalStateException();
-              }
-              openData.countBootClassPath();
-
-              data.add(openData);
-              openData = null;
-          }
-      }
-  }
-}
diff --git a/tools/preload2/src/com/android/preload/DumpTableModel.java b/tools/preload2/src/com/android/preload/DumpTableModel.java
deleted file mode 100644
index d97cbf0..0000000
--- a/tools/preload2/src/com/android/preload/DumpTableModel.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 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 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.preload;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.table.AbstractTableModel;
-
-/**
- * A table model for collected DumpData. This is both the internal storage as well as the model
- * for display.
- */
-public class DumpTableModel extends AbstractTableModel {
-
-    private List<DumpData> data = new ArrayList<DumpData>();
-
-    public void addData(DumpData d) {
-        data.add(d);
-        fireTableRowsInserted(data.size() - 1, data.size() - 1);
-    }
-
-    public void clear() {
-        int size = data.size();
-        if (size > 0) {
-            data.clear();
-            fireTableRowsDeleted(0, size - 1);
-        }
-    }
-
-    public List<DumpData> getData() {
-        return data;
-    }
-
-    @Override
-    public int getRowCount() {
-        return data.size();
-    }
-
-    @Override
-    public int getColumnCount() {
-        return 4;
-    }
-
-    @Override
-    public String getColumnName(int column) {
-        switch (column) {
-            case 0:
-                return "Package";
-            case 1:
-                return "Date";
-            case 2:
-                return "# All Classes";
-            case 3:
-                return "# Boot Classpath Classes";
-
-            default:
-                throw new IndexOutOfBoundsException(String.valueOf(column));
-        }
-    }
-
-    @Override
-    public Object getValueAt(int rowIndex, int columnIndex) {
-        DumpData d = data.get(rowIndex);
-        switch (columnIndex) {
-            case 0:
-                return d.packageName;
-            case 1:
-                return d.date;
-            case 2:
-                return d.dumpData.size();
-            case 3:
-                return d.bcpClasses;
-
-            default:
-                throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java
deleted file mode 100644
index 2265e95..0000000
--- a/tools/preload2/src/com/android/preload/Main.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 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 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.preload;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.actions.ClearTableAction;
-import com.android.preload.actions.ComputeThresholdAction;
-import com.android.preload.actions.ComputeThresholdXAction;
-import com.android.preload.actions.DeviceSpecific;
-import com.android.preload.actions.ExportAction;
-import com.android.preload.actions.ImportAction;
-import com.android.preload.actions.ReloadListAction;
-import com.android.preload.actions.RunMonkeyAction;
-import com.android.preload.actions.ScanAllPackagesAction;
-import com.android.preload.actions.ScanPackageAction;
-import com.android.preload.actions.ShowDataAction;
-import com.android.preload.actions.WritePreloadedClassesAction;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-import com.android.preload.classdataretrieval.hprof.Hprof;
-import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
-import com.android.preload.ui.IUI;
-import com.android.preload.ui.SequenceUI;
-import com.android.preload.ui.SwingUI;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-import javax.swing.Action;
-import javax.swing.DefaultListModel;
-
-public class Main {
-
-    /**
-     * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is
-     * off for now.
-     */
-    public final static boolean ENABLE_TRACING = false;
-
-    /**
-     * Ten-second timeout.
-     */
-    public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
-
-    /**
-     * Hprof timeout. Two minutes.
-     */
-    public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000;
-
-    private IDevice device;
-    private static ClientUtils clientUtils;
-
-    private DumpTableModel dataTableModel;
-    private DefaultListModel<Client> clientListModel;
-
-    private IUI ui;
-
-    // Actions that need to be updated once a device is selected.
-    private Collection<DeviceSpecific> deviceSpecificActions;
-
-    // Current main instance.
-    private static Main top;
-    private static boolean useJdwpClassDataRetriever = false;
-
-    public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|"
-            + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|"
-            + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" +
-
-
-            // Threads
-            "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|"
-            + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
-            + "(.*\\$NoPreloadHolder$)";
-
-    public final static String SCAN_ALL_CMD = "scan-all";
-    public final static String SCAN_PACKAGE_CMD = "scan";
-    public final static String COMPUTE_FILE_CMD = "comp";
-    public final static String EXPORT_CMD = "export";
-    public final static String IMPORT_CMD = "import";
-    public final static String WRITE_CMD = "write";
-
-    /**
-     * @param args
-     */
-    public static void main(String[] args) {
-        Main m;
-        if (args.length > 0 && args[0].equals("--seq")) {
-            m = createSequencedMain(args);
-        } else {
-            m = new Main(new SwingUI());
-        }
-
-        top = m;
-        m.startUp();
-    }
-
-    public Main(IUI ui) {
-        this.ui = ui;
-
-        clientListModel = new DefaultListModel<Client>();
-        dataTableModel = new DumpTableModel();
-
-        clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS);  // Client utils with 10s timeout.
-
-        List<Action> actions = new ArrayList<Action>();
-        actions.add(new ReloadListAction(clientUtils, null, clientListModel));
-        actions.add(new ClearTableAction(dataTableModel));
-        actions.add(new RunMonkeyAction(null, dataTableModel));
-        actions.add(new ScanPackageAction(clientUtils, null, dataTableModel));
-        actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel));
-        actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2,
-                CLASS_PRELOAD_BLACKLIST));
-        actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1,
-                null));
-        actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
-                CLASS_PRELOAD_BLACKLIST));
-        actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel));
-        actions.add(new ShowDataAction(dataTableModel));
-        actions.add(new ImportAction(dataTableModel));
-        actions.add(new ExportAction(dataTableModel));
-
-        deviceSpecificActions = new ArrayList<DeviceSpecific>();
-        for (Action a : actions) {
-            if (a instanceof DeviceSpecific) {
-                deviceSpecificActions.add((DeviceSpecific)a);
-            }
-        }
-
-        ui.prepare(clientListModel, dataTableModel, actions);
-    }
-
-    /**
-     * @param args
-     * @return
-     */
-    private static Main createSequencedMain(String[] args) {
-        SequenceUI ui = new SequenceUI();
-        Main main = new Main(ui);
-
-        Iterator<String> it = Arrays.asList(args).iterator();
-        it.next();  // --seq
-        // Setup
-        ui.choice("#" + it.next());  // Device.
-        ui.confirmNo();              // Prepare: no.
-        // Actions
-        try {
-            while (it.hasNext()) {
-                String op = it.next();
-                // Operation: Scan a single package
-                if (SCAN_PACKAGE_CMD.equals(op)) {
-                    System.out.println("Scanning package.");
-                    ui.action(ScanPackageAction.class);
-                    ui.client(it.next());
-                // Operation: Scan all packages
-                } else if (SCAN_ALL_CMD.equals(op)) {
-                    System.out.println("Scanning all packages.");
-                    ui.action(ScanAllPackagesAction.class);
-                // Operation: Export the output to a file
-                } else if (EXPORT_CMD.equals(op)) {
-                    System.out.println("Exporting data.");
-                    ui.action(ExportAction.class);
-                    ui.output(new File(it.next()));
-                // Operation: Import the input from a file or directory
-                } else if (IMPORT_CMD.equals(op)) {
-                    System.out.println("Importing data.");
-                    File file = new File(it.next());
-                    if (!file.exists()) {
-                        throw new RuntimeException(
-                                String.format("File does not exist, %s.", file.getAbsolutePath()));
-                    } else if (file.isFile()) {
-                        ui.action(ImportAction.class);
-                        ui.input(file);
-                    } else if (file.isDirectory()) {
-                        for (File content : file.listFiles()) {
-                            ui.action(ImportAction.class);
-                            ui.input(content);
-                        }
-                    }
-                // Operation: Compute preloaded classes with specific threshold
-                } else if (COMPUTE_FILE_CMD.equals(op)) {
-                    System.out.println("Compute preloaded classes.");
-                    ui.action(ComputeThresholdXAction.class);
-                    ui.input(it.next());
-                    ui.confirmYes();
-                    ui.output(new File(it.next()));
-                // Operation: Write preloaded classes from a specific file
-                } else if (WRITE_CMD.equals(op)) {
-                    System.out.println("Writing preloaded classes.");
-                    ui.action(WritePreloadedClassesAction.class);
-                    ui.input(new File(it.next()));
-                }
-            }
-        } catch (NoSuchElementException e) {
-            System.out.println("Failed to parse action sequence correctly.");
-            throw e;
-        }
-
-        return main;
-    }
-
-    public static IUI getUI() {
-        return top.ui;
-    }
-
-    public static ClassDataRetriever getClassDataRetriever() {
-        if (useJdwpClassDataRetriever) {
-            return new JDWPClassDataRetriever();
-        } else {
-            return new Hprof(HPROF_TIMEOUT_MILLIS);
-        }
-    }
-
-    public IDevice getDevice() {
-        return device;
-    }
-
-    public void setDevice(IDevice device) {
-        this.device = device;
-        for (DeviceSpecific ds : deviceSpecificActions) {
-            ds.setDevice(device);
-        }
-    }
-
-    public DefaultListModel<Client> getClientListModel() {
-        return clientListModel;
-    }
-
-    static class DeviceWrapper {
-        IDevice device;
-
-        public DeviceWrapper(IDevice d) {
-            device = d;
-        }
-
-        @Override
-        public String toString() {
-            return device.getName() + " (#" + device.getSerialNumber() + ")";
-        }
-    }
-
-    private void startUp() {
-        getUI().showWaitDialog();
-        initDevice();
-
-        // Load clients.
-        new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
-
-        getUI().hideWaitDialog();
-        getUI().ready();
-    }
-
-    private void initDevice() {
-        DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS);
-
-        IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS);
-        if (devices == null || devices.length == 0) {
-            throw new RuntimeException("Could not find any devices...");
-        }
-
-        getUI().hideWaitDialog();
-
-        DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length];
-        for (int i = 0; i < devices.length; i++) {
-            deviceWrappers[i] = new DeviceWrapper(devices[i]);
-        }
-
-        DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device",
-                deviceWrappers);
-        if (ret != null) {
-            setDevice(ret.device);
-        } else {
-            System.exit(0);
-        }
-
-        boolean prepare = Main.getUI().showConfirmDialog("Prepare device?",
-                "Do you want to prepare the device? This is highly recommended.");
-        if (prepare) {
-            String buildType = DeviceUtils.getBuildType(device);
-            if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) {
-                Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType
-                        + ")");
-                return;
-            }
-            if (DeviceUtils.hasPrebuiltBootImage(device)) {
-                Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot "
-                        + "image!");
-                return;
-            }
-
-            if (ENABLE_TRACING) {
-                DeviceUtils.enableTracing(device);
-            }
-
-            Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
-                    + "long time. Please be patient.");
-            boolean success = false;
-            try {
-                success = DeviceUtils.overwritePreloaded(device, null, 15 * 60);
-            } catch (Exception e) {
-                System.err.println(e);
-            } finally {
-                if (!success) {
-                    Main.getUI().showMessageDialog(
-                            "Removing preloaded-classes failed unexpectedly!");
-                }
-            }
-        }
-    }
-
-    public static Map<String, String> findAndGetClassData(IDevice device, String packageName)
-            throws Exception {
-        Client client = clientUtils.findClient(device, packageName, -1);
-        if (client == null) {
-            throw new RuntimeException("Could not find client...");
-        }
-        System.out.println("Found client: " + client);
-
-        return getClassDataRetriever().getClassData(client);
-    }
-
-}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
deleted file mode 100644
index 5787d85..0000000
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.preload.Main;
-import java.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-
-public abstract class AbstractThreadedAction extends AbstractAction implements Runnable {
-
-    protected AbstractThreadedAction(String title) {
-        super(title);
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        if (Main.getUI().isSingleThreaded()) {
-            run();
-        } else {
-            new Thread(this).start();
-        }
-    }
-
-}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
deleted file mode 100644
index 7906417..0000000
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-
-import java.awt.event.ActionEvent;
-
-public abstract class AbstractThreadedDeviceSpecificAction extends AbstractThreadedAction
-        implements DeviceSpecific {
-
-    protected IDevice device;
-
-    protected AbstractThreadedDeviceSpecificAction(String title, IDevice device) {
-        super(title);
-        this.device = device;
-    }
-
-    @Override
-    public void setDevice(IDevice device) {
-        this.device = device;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        if (device == null) {
-            return;
-        }
-        super.actionPerformed(e);
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java b/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
deleted file mode 100644
index c0e4795..0000000
--- a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.preload.DumpTableModel;
-
-import java.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-
-public class ClearTableAction extends AbstractAction {
-    private final DumpTableModel dataTableModel;
-
-    public ClearTableAction(DumpTableModel dataTableModel) {
-        super("Clear");
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        dataTableModel.clear();
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
deleted file mode 100644
index 3a7f7f7..0000000
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import javax.swing.AbstractAction;
-
-/**
- * Compute an intersection of classes from the given data. A class is in the intersection if it
- * appears in at least the number of threshold given packages. An optional blacklist can be
- * used to filter classes from the intersection.
- */
-public class ComputeThresholdAction extends AbstractThreadedAction {
-    protected int threshold;
-    private Pattern blacklist;
-    private DumpTableModel dataTableModel;
-
-    /**
-     * Create an action with the given parameters. The blacklist is a regular expression
-     * that filters classes.
-     */
-    public ComputeThresholdAction(String name, DumpTableModel dataTableModel, int threshold,
-            String blacklist) {
-        super(name);
-        this.dataTableModel = dataTableModel;
-        this.threshold = threshold;
-        if (blacklist != null) {
-            this.blacklist = Pattern.compile(blacklist);
-        }
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        List<DumpData> data = dataTableModel.getData();
-        if (data.size() == 0) {
-            Main.getUI().showMessageDialog("No data available, please scan packages or run "
-                    + "monkeys.");
-            return;
-        }
-        if (data.size() == 1) {
-            Main.getUI().showMessageDialog("Cannot compute list from only one data set, please "
-                    + "scan packages or run monkeys.");
-            return;
-        }
-
-        super.actionPerformed(e);
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        Map<String, Set<String>> uses = new HashMap<String, Set<String>>();
-        for (DumpData d : dataTableModel.getData()) {
-            Main.getUI().updateWaitDialog("Merging " + d.getPackageName());
-            updateClassUse(d.getPackageName(), uses, getBootClassPathClasses(d.getDumpData()));
-        }
-
-        Main.getUI().updateWaitDialog("Computing thresholded set");
-        Set<String> result = fromThreshold(uses, blacklist, threshold);
-        Main.getUI().hideWaitDialog();
-
-        boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size()
-                + " classes, would you like to save to disk?", "Save?");
-        if (ret) {
-            File f = Main.getUI().showSaveDialog();
-            if (f != null) {
-                saveSet(result, f);
-            }
-        }
-    }
-
-    private Set<String> fromThreshold(Map<String, Set<String>> classUses, Pattern blacklist,
-            int threshold) {
-        TreeSet<String> ret = new TreeSet<>(); // TreeSet so it's nicely ordered by name.
-
-        for (Map.Entry<String, Set<String>> e : classUses.entrySet()) {
-            if (e.getValue().size() >= threshold) {
-                if (blacklist == null || !blacklist.matcher(e.getKey()).matches()) {
-                    ret.add(e.getKey());
-                }
-            }
-        }
-
-        return ret;
-    }
-
-    private static void updateClassUse(String pkg, Map<String, Set<String>> classUses,
-            Set<String> classes) {
-        for (String className : classes) {
-            Set<String> old = classUses.get(className);
-            if (old == null) {
-                classUses.put(className, new HashSet<String>());
-            }
-            classUses.get(className).add(pkg);
-        }
-    }
-
-    private static Set<String> getBootClassPathClasses(Map<String, String> source) {
-        Set<String> ret = new HashSet<>();
-        for (Map.Entry<String, String> e : source.entrySet()) {
-            if (e.getValue() == null) {
-                ret.add(e.getKey());
-            }
-        }
-        return ret;
-    }
-
-    private static void saveSet(Set<String> result, File f) {
-        try {
-            PrintWriter out = new PrintWriter(f);
-            for (String s : result) {
-                out.println(s);
-            }
-            out.close();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
deleted file mode 100644
index 3ec0a4c..0000000
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-public class ComputeThresholdXAction extends ComputeThresholdAction {
-
-    public ComputeThresholdXAction(String name, DumpTableModel dataTableModel,
-            String blacklist) {
-        super(name, dataTableModel, 1, blacklist);
-    }
-
-    @Override
-    public void run() {
-        String value = Main.getUI().showInputDialog("Threshold?");
-
-        if (value != null) {
-            try {
-                threshold = Integer.parseInt(value);
-                super.run();
-            } catch (Exception exc) {
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java b/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
deleted file mode 100644
index 35a8f26..0000000
--- a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-
-/**
- * Marks an action as being device-specific. The user must set the device through the specified
- * method if the device selection changes.
- *
- * Implementors must tolerate a null device (for example, with a no-op). This includes calling
- * any methods before setDevice has been called.
- */
-public interface DeviceSpecific {
-
-    /**
-     * Set the device that should be used. Note that there is no restriction on calling other
-     * methods of the implementor before a setDevice call. Neither is device guaranteed to be
-     * non-null.
-     *
-     * @param device The device to use going forward.
-     */
-    public void setDevice(IDevice device);
-}
diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java
deleted file mode 100644
index 848a568..0000000
--- a/tools/preload2/src/com/android/preload/actions/ExportAction.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.preload.DumpDataIO;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.io.PrintWriter;
-
-public class ExportAction extends AbstractThreadedAction {
-    private File lastSaveFile;
-    private DumpTableModel dataTableModel;
-
-    public ExportAction(DumpTableModel dataTableModel) {
-        super("Export data");
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        lastSaveFile = Main.getUI().showSaveDialog();
-        if (lastSaveFile != null) {
-            super.actionPerformed(e);
-        }
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        String serialized = DumpDataIO.serialize(dataTableModel.getData());
-
-        if (serialized != null) {
-            try {
-                PrintWriter out = new PrintWriter(lastSaveFile);
-                out.println(serialized);
-                out.close();
-
-                Main.getUI().hideWaitDialog();
-            } catch (Exception e) {
-                Main.getUI().hideWaitDialog();
-                Main.getUI().showMessageDialog("Failed writing: " + e.getMessage());
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java
deleted file mode 100644
index bfeeb83..0000000
--- a/tools/preload2/src/com/android/preload/actions/ImportAction.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpDataIO;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.Collection;
-
-import javax.swing.AbstractAction;
-
-public class ImportAction extends AbstractThreadedAction {
-    private File[] lastOpenFiles;
-    private DumpTableModel dataTableModel;
-
-    public ImportAction(DumpTableModel dataTableModel) {
-        super("Import data");
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        lastOpenFiles = Main.getUI().showOpenDialog(true);
-        if (lastOpenFiles != null) {
-            super.actionPerformed(e);
-        }
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        try {
-            for (File f : lastOpenFiles) {
-                try {
-                    Collection<DumpData> data = DumpDataIO.deserialize(f);
-
-                    for (DumpData d : data) {
-                        dataTableModel.addData(d);
-                    }
-                } catch (Exception e) {
-                    Main.getUI().showMessageDialog("Failed reading: " + e.getMessage());
-                }
-            }
-        } finally {
-            Main.getUI().hideWaitDialog();
-        }
-
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java b/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
deleted file mode 100644
index 29f0557..0000000
--- a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-
-import java.util.Arrays;
-import java.util.Comparator;
-
-import javax.swing.DefaultListModel;
-
-public class ReloadListAction extends AbstractThreadedDeviceSpecificAction {
-
-    private ClientUtils clientUtils;
-    private final DefaultListModel<Client> clientListModel;
-
-    public ReloadListAction(ClientUtils utils, IDevice device,
-            DefaultListModel<Client> clientListModel) {
-        super("Reload", device);
-        this.clientUtils = utils;
-        this.clientListModel = clientListModel;
-    }
-
-    @Override
-    public void run() {
-        Client[] clients = clientUtils.findAllClients(device);
-        if (clients != null) {
-            Arrays.sort(clients, new ClientComparator());
-        }
-        clientListModel.removeAllElements();
-        for (Client c : clients) {
-            clientListModel.addElement(c);
-        }
-    }
-
-    private static class ClientComparator implements Comparator<Client> {
-
-        @Override
-        public int compare(Client o1, Client o2) {
-            String s1 = o1.getClientData().getClientDescription();
-            String s2 = o2.getClientData().getClientDescription();
-
-            if (s1 == null || s2 == null) {
-                // Not good, didn't get all data?
-                return (s1 == null) ? -1 : 1;
-            }
-
-            return s1.compareTo(s2);
-        }
-
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
deleted file mode 100644
index 29464fc..0000000
--- a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-import com.android.preload.DeviceUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.util.Date;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.swing.AbstractAction;
-
-public class RunMonkeyAction extends AbstractAction implements DeviceSpecific {
-
-    private final static String DEFAULT_MONKEY_PACKAGES =
-            "com.android.calendar,com.android.gallery3d";
-
-    private IDevice device;
-    private DumpTableModel dataTableModel;
-
-    public RunMonkeyAction(IDevice device, DumpTableModel dataTableModel) {
-        super("Run monkey");
-        this.device = device;
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void setDevice(IDevice device) {
-        this.device = device;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        String packages = Main.getUI().showInputDialog("Please enter packages name to run with"
-                + " the monkey, or leave empty for default.");
-        if (packages == null) {
-            return;
-        }
-        if (packages.isEmpty()) {
-            packages = DEFAULT_MONKEY_PACKAGES;
-        }
-        Runnable r = new RunMonkeyRunnable(packages);
-        if (Main.getUI().isSingleThreaded()) {
-            r.run();
-        } else {
-            new Thread(r).start();
-        }
-    }
-
-    private class RunMonkeyRunnable implements Runnable {
-
-        private String packages;
-        private final static int ITERATIONS = 1000;
-
-        public RunMonkeyRunnable(String packages) {
-            this.packages = packages;
-        }
-
-        @Override
-        public void run() {
-            Main.getUI().showWaitDialog();
-
-            try {
-                String pkgs[] = packages.split(",");
-
-                for (String pkg : pkgs) {
-                    Main.getUI().updateWaitDialog("Running monkey on " + pkg);
-
-                    try {
-                        // Stop running app.
-                        forceStop(pkg);
-
-                        // Little bit of breather here.
-                        try {
-                            Thread.sleep(1000);
-                        } catch (Exception e) {
-                        }
-
-                        DeviceUtils.doShell(device, "monkey -p " + pkg + " " + ITERATIONS, 1,
-                                TimeUnit.MINUTES);
-
-                        Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-                        Map<String, String> data = Main.findAndGetClassData(device, pkg);
-                        DumpData dumpData = new DumpData(pkg, data, new Date());
-                        dataTableModel.addData(dumpData);
-                    } catch (Exception e) {
-                        e.printStackTrace();
-                    } finally {
-                        // Stop running app.
-                        forceStop(pkg);
-                    }
-                }
-            } finally {
-                Main.getUI().hideWaitDialog();
-            }
-        }
-
-        private void forceStop(String packageName) {
-            // Stop running app.
-            DeviceUtils.doShell(device, "force-stop " + packageName, 5, TimeUnit.SECONDS);
-            DeviceUtils.doShell(device, "kill " + packageName, 5, TimeUnit.SECONDS);
-            DeviceUtils.doShell(device, "kill `pid " + packageName + "`", 5, TimeUnit.SECONDS);
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java b/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
deleted file mode 100644
index d74b8a3..0000000
--- a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.util.Date;
-import java.util.Map;
-
-public class ScanAllPackagesAction extends AbstractThreadedDeviceSpecificAction {
-
-    private ClientUtils clientUtils;
-    private DumpTableModel dataTableModel;
-
-    public ScanAllPackagesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
-        super("Scan all packages", device);
-        this.clientUtils = utils;
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        try {
-            Client[] clients = clientUtils.findAllClients(device);
-            for (Client c : clients) {
-                String pkg = c.getClientData().getClientDescription();
-                Main.getUI().showWaitDialog();
-                Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-
-                try {
-                    Map<String, String> data = Main.getClassDataRetriever().getClassData(c);
-                    DumpData dumpData = new DumpData(pkg, data, new Date());
-                    dataTableModel.addData(dumpData);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-            }
-        } finally {
-            Main.getUI().hideWaitDialog();
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java b/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
deleted file mode 100644
index 98492bd..0000000
--- a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.util.Date;
-import java.util.Map;
-
-public class ScanPackageAction extends AbstractThreadedDeviceSpecificAction {
-
-    private ClientUtils clientUtils;
-    private DumpTableModel dataTableModel;
-
-    public ScanPackageAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
-        super("Scan package", device);
-        this.clientUtils = utils;
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        try {
-            Client client = Main.getUI().getSelectedClient();
-            if (client != null) {
-                work(client);
-            } else {
-                Client[] clients = clientUtils.findAllClients(device);
-                if (clients.length > 0) {
-                    ClientWrapper[] clientWrappers = new ClientWrapper[clients.length];
-                    for (int i = 0; i < clientWrappers.length; i++) {
-                        clientWrappers[i] = new ClientWrapper(clients[i]);
-                    }
-                    Main.getUI().hideWaitDialog();
-
-                    ClientWrapper ret = Main.getUI().showChoiceDialog("Choose a package to scan",
-                            "Choose package",
-                            clientWrappers);
-                    if (ret != null) {
-                        work(ret.client);
-                    }
-                }
-            }
-        } finally {
-            Main.getUI().hideWaitDialog();
-        }
-    }
-
-    private void work(Client c) {
-        String pkg = c.getClientData().getClientDescription();
-        Main.getUI().showWaitDialog();
-        Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-
-        try {
-            Map<String, String> data = Main.findAndGetClassData(device, pkg);
-            DumpData dumpData = new DumpData(pkg, data, new Date());
-            dataTableModel.addData(dumpData);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    private static class ClientWrapper {
-        private Client client;
-
-        public ClientWrapper(Client c) {
-            client = c;
-        }
-
-        @Override
-        public String toString() {
-            return client.getClientData().getClientDescription() + " (pid "
-                    + client.getClientData().getPid() + ")";
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java b/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
deleted file mode 100644
index 2bb175f..0000000
--- a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.swing.AbstractAction;
-import javax.swing.JFrame;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-
-public class ShowDataAction extends AbstractAction {
-    private DumpTableModel dataTableModel;
-
-    public ShowDataAction(DumpTableModel dataTableModel) {
-        super("Show data");
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        // TODO(agampe): Auto-generated method stub
-        int selRow = Main.getUI().getSelectedDataTableRow();
-        if (selRow != -1) {
-            DumpData data = dataTableModel.getData().get(selRow);
-            Map<String, Set<String>> inv = data.invertData();
-
-            StringBuilder builder = new StringBuilder();
-
-            // First bootclasspath.
-            add(builder, "Boot classpath:", inv.get(null));
-
-            // Now everything else.
-            for (String k : inv.keySet()) {
-                if (k != null) {
-                    builder.append("==================\n\n");
-                    add(builder, k, inv.get(k));
-                }
-            }
-
-            JFrame newFrame = new JFrame(data.getPackageName() + " " + data.getDate());
-            newFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
-            newFrame.getContentPane().add(new JScrollPane(new JTextArea(builder.toString())),
-                    BorderLayout.CENTER);
-            newFrame.setSize(800, 600);
-            newFrame.setLocationRelativeTo(null);
-            newFrame.setVisible(true);
-        }
-    }
-
-    private void add(StringBuilder builder, String head, Set<String> set) {
-        builder.append(head);
-        builder.append('\n');
-        addSet(builder, set);
-        builder.append('\n');
-    }
-
-    private void addSet(StringBuilder builder, Set<String> set) {
-        if (set == null) {
-            builder.append("  NONE\n");
-            return;
-        }
-        List<String> sorted = new ArrayList<>(set);
-        Collections.sort(sorted);
-        for (String s : sorted) {
-            builder.append(s);
-            builder.append('\n');
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java b/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java
deleted file mode 100644
index 9b97f11..0000000
--- a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DeviceUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.Date;
-import java.util.Map;
-
-public class WritePreloadedClassesAction extends AbstractThreadedDeviceSpecificAction {
-    private File preloadedClassFile;
-
-    public WritePreloadedClassesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
-        super("Write preloaded classes action", device);
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        File[] files = Main.getUI().showOpenDialog(true);
-        if (files != null && files.length > 0) {
-            preloadedClassFile = files[0];
-            super.actionPerformed(e);
-        }
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-        try {
-            // Write the new file with a 5-minute timeout
-            DeviceUtils.overwritePreloaded(device, preloadedClassFile, 5 * 60);
-        } catch (Exception e) {
-            System.err.println(e);
-        } finally {
-            Main.getUI().hideWaitDialog();
-        }
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
deleted file mode 100644
index 8d797ee..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.classdataretrieval.hprof;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData.IHprofDumpHandler;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class GeneralHprofDumpHandler implements IHprofDumpHandler {
-
-    private List<IHprofDumpHandler> handlers = new ArrayList<>();
-
-    public void addHandler(IHprofDumpHandler h) {
-      synchronized (handlers) {
-        handlers.add(h);
-      }
-    }
-
-    public void removeHandler(IHprofDumpHandler h) {
-      synchronized (handlers) {
-        handlers.remove(h);
-      }
-    }
-
-    private List<IHprofDumpHandler> getIterationList() {
-      synchronized (handlers) {
-        return new ArrayList<>(handlers);
-      }
-    }
-
-    @Override
-    public void onEndFailure(Client arg0, String arg1) {
-      List<IHprofDumpHandler> iterList = getIterationList();
-      for (IHprofDumpHandler h : iterList) {
-        h.onEndFailure(arg0, arg1);
-      }
-    }
-
-    @Override
-    public void onSuccess(String arg0, Client arg1) {
-      List<IHprofDumpHandler> iterList = getIterationList();
-      for (IHprofDumpHandler h : iterList) {
-        h.onSuccess(arg0, arg1);
-      }
-    }
-
-    @Override
-    public void onSuccess(byte[] arg0, Client arg1) {
-      List<IHprofDumpHandler> iterList = getIterationList();
-      for (IHprofDumpHandler h : iterList) {
-        h.onSuccess(arg0, arg1);
-      }
-    }
-  }
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
deleted file mode 100644
index 84ec8b7..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.classdataretrieval.hprof;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-import com.android.ddmlib.ClientData.IHprofDumpHandler;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-import com.android.preload.ui.NullProgressMonitor;
-import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Queries;
-import com.android.tools.perflib.heap.Snapshot;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class Hprof implements ClassDataRetriever {
-
-    private static GeneralHprofDumpHandler hprofHandler;
-
-    public static void init() {
-        synchronized(Hprof.class) {
-            if (hprofHandler == null) {
-                ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler());
-            }
-        }
-    }
-
-    public static File doHprof(Client client, int timeout) {
-        GetHprof gh = new GetHprof(client, timeout);
-        return gh.get();
-    }
-
-    /**
-     * Return a map of class names to class-loader names derived from the hprof dump.
-     *
-     * @param hprofLocalFile
-     */
-    public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception {
-        Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile));
-
-        Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null);
-        Map<String, String> retValue = new HashMap<String, String>();
-        for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) {
-            for (ClassObj c : e.getValue()) {
-                String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString();
-                String cName = c.getClassName();
-                int aDepth = 0;
-                while (cName.endsWith("[]")) {
-                    cName = cName.substring(0, cName.length()-2);
-                    aDepth++;
-                }
-                String newName = transformPrimitiveClass(cName);
-                if (aDepth > 0) {
-                    // Need to use kind-a descriptor syntax. If it was transformed, it is primitive.
-                    if (newName.equals(cName)) {
-                        newName = "L" + newName + ";";
-                    }
-                    for (int i = 0; i < aDepth; i++) {
-                        newName = "[" + newName;
-                    }
-                }
-                retValue.put(newName, cl);
-            }
-        }
-
-        // Free up memory.
-        snapshot.dispose();
-
-        return retValue;
-    }
-
-    private static Map<String, String> primitiveMapping;
-
-    static {
-        primitiveMapping = new HashMap<>();
-        primitiveMapping.put("boolean", "Z");
-        primitiveMapping.put("byte", "B");
-        primitiveMapping.put("char", "C");
-        primitiveMapping.put("double", "D");
-        primitiveMapping.put("float", "F");
-        primitiveMapping.put("int", "I");
-        primitiveMapping.put("long", "J");
-        primitiveMapping.put("short", "S");
-        primitiveMapping.put("void", "V");
-    }
-
-    private static String transformPrimitiveClass(String name) {
-        String rep = primitiveMapping.get(name);
-        if (rep != null) {
-            return rep;
-        }
-        return name;
-    }
-
-    private static class GetHprof implements IHprofDumpHandler {
-
-        private File target;
-        private long timeout;
-        private Client client;
-
-        public GetHprof(Client client, long timeout) {
-            this.client = client;
-            this.timeout = timeout;
-        }
-
-        public File get() {
-            synchronized (this) {
-                hprofHandler.addHandler(this);
-                client.dumpHprof();
-                if (target == null) {
-                    try {
-                        wait(timeout);
-                    } catch (Exception e) {
-                        System.out.println(e);
-                    }
-                }
-            }
-
-            hprofHandler.removeHandler(this);
-            return target;
-        }
-
-        private void wakeUp() {
-            synchronized (this) {
-                notifyAll();
-            }
-        }
-
-        @Override
-        public void onEndFailure(Client arg0, String arg1) {
-            System.out.println("GetHprof.onEndFailure");
-            if (client == arg0) {
-                wakeUp();
-            }
-        }
-
-        private static File createTargetFile() {
-            try {
-                return File.createTempFile("ddms", ".hprof");
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        @Override
-        public void onSuccess(String arg0, Client arg1) {
-            System.out.println("GetHprof.onSuccess");
-            if (client == arg1) {
-                try {
-                    target = createTargetFile();
-                    arg1.getDevice().getSyncService().pullFile(arg0,
-                            target.getAbsoluteFile().toString(), new NullProgressMonitor());
-                } catch (Exception e) {
-                    if (target != null) {
-                        target.delete();
-                    }
-                    e.printStackTrace();
-                    target = null;
-                }
-                wakeUp();
-            }
-        }
-
-        @Override
-        public void onSuccess(byte[] arg0, Client arg1) {
-            System.out.println("GetHprof.onSuccess");
-            if (client == arg1) {
-                try {
-                    target = createTargetFile();
-                    BufferedOutputStream out =
-                            new BufferedOutputStream(new FileOutputStream(target));
-                    out.write(arg0);
-                    out.close();
-                } catch (Exception e) {
-                    if (target != null) {
-                        target.delete();
-                    }
-                    e.printStackTrace();
-                    target = null;
-                }
-                wakeUp();
-            }
-        }
-    }
-
-    private int timeout;
-
-    public Hprof(int timeout) {
-        this.timeout = timeout;
-    }
-
-    @Override
-    public Map<String, String> getClassData(Client client) {
-        File hprofLocalFile = Hprof.doHprof(client, timeout);
-        if (hprofLocalFile == null) {
-            throw new RuntimeException("Failed getting dump...");
-        }
-        System.out.println("Dump file is " + hprofLocalFile);
-
-        try {
-            return analyzeHprof(hprofLocalFile);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        } finally {
-            hprofLocalFile.delete();
-        }
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
deleted file mode 100644
index dbd4c89..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.classdataretrieval.jdwp;
-
-import com.android.ddmlib.Client;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-
-import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
-import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
-import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
-import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
-import org.apache.harmony.jpda.tests.share.JPDALogWriter;
-import org.apache.harmony.jpda.tests.share.JPDATestOptions;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever {
-
-    private final Client client;
-
-    public JDWPClassDataRetriever() {
-        this(null);
-    }
-
-    public JDWPClassDataRetriever(Client client) {
-        this.client = client;
-    }
-
-
-    @Override
-    protected String getDebuggeeClassName() {
-        return "<unset>";
-    }
-
-    @Override
-    public Map<String, String> getClassData(Client client) {
-        return new JDWPClassDataRetriever(client).retrieve();
-    }
-
-    private Map<String, String> retrieve() {
-        if (client == null) {
-            throw new IllegalStateException();
-        }
-
-        settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort()));
-        settings.setDebuggeeSuspend("n");
-
-        logWriter = new JPDALogWriter(System.out, "", false);
-
-        try {
-            internalSetUp();
-
-            return retrieveImpl();
-        } catch (Exception e) {
-            e.printStackTrace();
-            return null;
-        } finally {
-            internalTearDown();
-        }
-    }
-
-    private Map<String, String> retrieveImpl() {
-        try {
-            // Suspend the app.
-            {
-                CommandPacket packet = new CommandPacket(
-                        JDWPCommands.VirtualMachineCommandSet.CommandSetID,
-                        JDWPCommands.VirtualMachineCommandSet.SuspendCommand);
-                ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-                if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
-                    return null;
-                }
-            }
-
-            // List all classes.
-            CommandPacket packet = new CommandPacket(
-                    JDWPCommands.VirtualMachineCommandSet.CommandSetID,
-                    JDWPCommands.VirtualMachineCommandSet.AllClassesCommand);
-            ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-
-            if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
-                return null;
-            }
-
-            int classCount = reply.getNextValueAsInt();
-            System.out.println("Runtime reported " + classCount + " classes.");
-
-            Map<Long, String> classes = new HashMap<Long, String>();
-            Map<Long, String> arrayClasses = new HashMap<Long, String>();
-
-            for (int i = 0; i < classCount; i++) {
-                byte refTypeTag = reply.getNextValueAsByte();
-                long typeID = reply.getNextValueAsReferenceTypeID();
-                String signature = reply.getNextValueAsString();
-                /* int status = */ reply.getNextValueAsInt();
-
-                switch (refTypeTag) {
-                    case JDWPConstants.TypeTag.CLASS:
-                    case JDWPConstants.TypeTag.INTERFACE:
-                        classes.put(typeID, signature);
-                        break;
-
-                    case JDWPConstants.TypeTag.ARRAY:
-                        arrayClasses.put(typeID, signature);
-                        break;
-                }
-            }
-
-            Map<String, String> result = new HashMap<String, String>();
-
-            // Parse all classes.
-            for (Map.Entry<Long, String> entry : classes.entrySet()) {
-                long typeID = entry.getKey();
-                String signature = entry.getValue();
-
-                if (!checkClass(typeID, signature, result)) {
-                    System.err.println("Issue investigating " + signature);
-                }
-            }
-
-            // For arrays, look at the leaf component type.
-            for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) {
-                long typeID = entry.getKey();
-                String signature = entry.getValue();
-
-                if (!checkArrayClass(typeID, signature, result)) {
-                    System.err.println("Issue investigating " + signature);
-                }
-            }
-
-            return result;
-        } finally {
-            // Resume the app.
-            {
-                CommandPacket packet = new CommandPacket(
-                        JDWPCommands.VirtualMachineCommandSet.CommandSetID,
-                        JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
-                /* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet);
-            }
-        }
-    }
-
-    private boolean checkClass(long typeID, String signature, Map<String, String> result) {
-        CommandPacket packet = new CommandPacket(
-                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
-                JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
-        packet.setNextValueAsReferenceTypeID(typeID);
-        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-        if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
-            return false;
-        }
-
-        long classLoaderID = reply.getNextValueAsObjectID();
-
-        // TODO: Investigate the classloader to have a better string?
-        String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
-
-        result.put(getClassName(signature), classLoaderString);
-
-        return true;
-    }
-
-    private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) {
-        // Classloaders of array classes are the same as the component class'.
-        CommandPacket packet = new CommandPacket(
-                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
-                JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
-        packet.setNextValueAsReferenceTypeID(typeID);
-        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-        if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
-            return false;
-        }
-
-        long classLoaderID = reply.getNextValueAsObjectID();
-
-        // TODO: Investigate the classloader to have a better string?
-        String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
-
-        // For array classes, we *need* the signature directly.
-        result.put(signature, classLoaderString);
-
-        return true;
-    }
-
-    private static String getClassName(String signature) {
-        String withoutLAndSemicolon = signature.substring(1, signature.length() - 1);
-        return withoutLAndSemicolon.replace('/', '.');
-    }
-
-
-    private static JPDATestOptions createTestOptions(String address) {
-        JPDATestOptions options = new JPDATestOptions();
-        options.setAttachConnectorKind();
-        options.setTimeout(1000);
-        options.setWaitingTime(1000);
-        options.setTransportAddress(address);
-        return options;
-    }
-
-    @Override
-    protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
-        return new PreloadDebugeeWrapper(settings, logWriter);
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
deleted file mode 100644
index b9df6d0..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.classdataretrieval.jdwp;
-
-import org.apache.harmony.jpda.tests.framework.LogWriter;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPManualDebuggeeWrapper;
-import org.apache.harmony.jpda.tests.share.JPDATestOptions;
-
-import java.io.IOException;
-
-public class PreloadDebugeeWrapper extends JDWPManualDebuggeeWrapper {
-
-    public PreloadDebugeeWrapper(JPDATestOptions options, LogWriter writer) {
-        super(options, writer);
-    }
-
-    @Override
-    protected Process launchProcess(String cmdLine) throws IOException {
-        return null;
-    }
-
-    @Override
-    protected void WaitForProcessExit(Process process) {
-    }
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/IUI.java b/tools/preload2/src/com/android/preload/ui/IUI.java
deleted file mode 100644
index 9371463..0000000
--- a/tools/preload2/src/com/android/preload/ui/IUI.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import java.io.File;
-import java.util.List;
-import javax.swing.Action;
-import javax.swing.ListModel;
-import javax.swing.table.TableModel;
-
-/**
- * UI abstraction for the tool. This allows a graphical mode, command line mode,
- * or silent mode.
- */
-public interface IUI {
-
-    void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
-            List<Action> actions);
-
-    void ready();
-
-    boolean isSingleThreaded();
-
-    Client getSelectedClient();
-
-    int getSelectedDataTableRow();
-
-    void showWaitDialog();
-
-    void updateWaitDialog(String s);
-
-    void hideWaitDialog();
-
-    void showMessageDialog(String s);
-
-    boolean showConfirmDialog(String title, String message);
-
-    String showInputDialog(String message);
-
-    <T> T showChoiceDialog(String title, String message, T[] choices);
-
-    File showSaveDialog();
-
-    File[] showOpenDialog(boolean multi);
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java b/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
deleted file mode 100644
index f45aad0..0000000
--- a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.ui;
-
-import com.android.ddmlib.SyncService.ISyncProgressMonitor;
-
-public class NullProgressMonitor implements ISyncProgressMonitor {
-
-    @Override
-    public void advance(int arg0) {}
-
-    @Override
-    public boolean isCanceled() {
-        return false;
-    }
-
-    @Override
-    public void start(int arg0) {}
-
-    @Override
-    public void startSubTask(String arg0) {}
-
-    @Override
-    public void stop() {}
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/ui/SequenceUI.java b/tools/preload2/src/com/android/preload/ui/SequenceUI.java
deleted file mode 100644
index dc6a4f3..0000000
--- a/tools/preload2/src/com/android/preload/ui/SequenceUI.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-import java.io.File;
-import java.util.LinkedList;
-import java.util.List;
-import javax.swing.Action;
-import javax.swing.ListModel;
-import javax.swing.table.TableModel;
-
-public class SequenceUI implements IUI {
-
-    private ListModel<Client> clientListModel;
-    @SuppressWarnings("unused")
-    private TableModel dataTableModel;
-    private List<Action> actions;
-
-    private List<Object> sequence = new LinkedList<>();
-
-    public SequenceUI() {
-    }
-
-    @Override
-    public boolean isSingleThreaded() {
-        return true;
-    }
-
-    @Override
-    public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
-            List<Action> actions) {
-        this.clientListModel = clientListModel;
-        this.dataTableModel = dataTableModel;
-        this.actions = actions;
-    }
-
-    public SequenceUI action(Action a) {
-        sequence.add(a);
-        return this;
-    }
-
-    public SequenceUI action(Class<? extends Action> actionClass) {
-        for (Action a : actions) {
-            if (actionClass.equals(a.getClass())) {
-                sequence.add(a);
-                return this;
-            }
-        }
-        throw new IllegalArgumentException("No action of class " + actionClass + " found.");
-    }
-
-    public SequenceUI confirmYes() {
-        sequence.add(Boolean.TRUE);
-        return this;
-    }
-
-    public SequenceUI confirmNo() {
-        sequence.add(Boolean.FALSE);
-        return this;
-    }
-
-    public SequenceUI input(String input) {
-        sequence.add(input);
-        return this;
-    }
-
-    public SequenceUI input(File... f) {
-        sequence.add(f);
-        return this;
-    }
-
-    public SequenceUI output(File f) {
-        sequence.add(f);
-        return this;
-    }
-
-    public SequenceUI tableRow(int i) {
-        sequence.add(i);
-        return this;
-    }
-
-    private class ClientSelector {
-        private String pkg;
-
-        public ClientSelector(String pkg) {
-            this.pkg = pkg;
-        }
-
-        public Client getClient() {
-            for (int i = 0; i < clientListModel.getSize(); i++) {
-                ClientData cd = clientListModel.getElementAt(i).getClientData();
-                if (cd != null) {
-                    String s = cd.getClientDescription();
-                    if (pkg.equals(s)) {
-                        return clientListModel.getElementAt(i);
-                    }
-                }
-            }
-            throw new RuntimeException("Didn't find client " + pkg);
-        }
-    }
-
-    public SequenceUI client(String pkg) {
-        sequence.add(new ClientSelector(pkg));
-        return this;
-    }
-
-    public SequenceUI choice(String pattern) {
-        sequence.add(pattern);
-        return this;
-    }
-
-    @Override
-    public void ready() {
-        // Run the actions.
-        // No iterator or foreach loop as the sequence will be emptied while running.
-        try {
-            while (!sequence.isEmpty()) {
-                Object next = sequence.remove(0);
-                if (next instanceof Action) {
-                    ((Action)next).actionPerformed(null);
-                } else {
-                    throw new IllegalStateException("Didn't expect a non-action: " + next);
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace(System.out);
-        }
-
-        // Now shut down.
-        System.exit(0);
-    }
-
-    @Override
-    public Client getSelectedClient() {
-        Object next = sequence.remove(0);
-        if (next instanceof ClientSelector) {
-            return ((ClientSelector)next).getClient();
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public int getSelectedDataTableRow() {
-        Object next = sequence.remove(0);
-        if (next instanceof Integer) {
-            return ((Integer)next).intValue();
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public void showWaitDialog() {
-    }
-
-    @Override
-    public void updateWaitDialog(String s) {
-        System.out.println(s);
-    }
-
-    @Override
-    public void hideWaitDialog() {
-    }
-
-    @Override
-    public void showMessageDialog(String s) {
-        System.out.println(s);
-    }
-
-    @Override
-    public boolean showConfirmDialog(String title, String message) {
-        Object next = sequence.remove(0);
-        if (next instanceof Boolean) {
-            return ((Boolean)next).booleanValue();
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public String showInputDialog(String message) {
-        Object next = sequence.remove(0);
-        if (next instanceof String) {
-            return (String)next;
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public <T> T showChoiceDialog(String title, String message, T[] choices) {
-        Object next = sequence.remove(0);
-        if (next instanceof String) {
-            String s = (String)next;
-            for (T t : choices) {
-                if (t.toString().contains(s)) {
-                    return t;
-                }
-            }
-            return null;
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public File showSaveDialog() {
-        Object next = sequence.remove(0);
-        if (next instanceof File) {
-            System.out.println(next);
-            return (File)next;
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public File[] showOpenDialog(boolean multi) {
-        Object next = sequence.remove(0);
-        if (next instanceof File[]) {
-            return (File[])next;
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/SwingUI.java b/tools/preload2/src/com/android/preload/ui/SwingUI.java
deleted file mode 100644
index cab3744..0000000
--- a/tools/preload2/src/com/android/preload/ui/SwingUI.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 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 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.preload.ui;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.io.File;
-import java.util.List;
-
-import javax.swing.Action;
-import javax.swing.DefaultListCellRenderer;
-import javax.swing.JDialog;
-import javax.swing.JFileChooser;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
-import javax.swing.JProgressBar;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.JToolBar;
-import javax.swing.ListModel;
-import javax.swing.SwingUtilities;
-import javax.swing.table.TableModel;
-
-public class SwingUI extends JFrame implements IUI {
-
-    private JList<Client> clientList;
-    private JTable dataTable;
-
-    // Shared file chooser, means the directory is retained.
-    private JFileChooser jfc;
-
-    public SwingUI() {
-        super("Preloaded-classes computation");
-    }
-
-    @Override
-    public boolean isSingleThreaded() {
-        return false;
-    }
-
-    @Override
-    public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
-            List<Action> actions) {
-        getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)),
-                BorderLayout.WEST);
-        clientList.setCellRenderer(new ClientListCellRenderer());
-        // clientList.addListSelectionListener(listener);
-
-        dataTable = new JTable(dataTableModel);
-        getContentPane().add(new JScrollPane(dataTable), BorderLayout.CENTER);
-
-        JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
-        for (Action a : actions) {
-            if (a == null) {
-                toolbar.addSeparator();
-            } else {
-                toolbar.add(a);
-            }
-        }
-        getContentPane().add(toolbar, BorderLayout.PAGE_START);
-
-        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-        setBounds(100, 100, 800, 600);
-
-        setVisible(true);
-    }
-
-    @Override
-    public void ready() {
-    }
-
-    @Override
-    public Client getSelectedClient() {
-        return clientList.getSelectedValue();
-    }
-
-    @Override
-    public int getSelectedDataTableRow() {
-        return dataTable.getSelectedRow();
-    }
-
-    private JDialog currentWaitDialog = null;
-
-    @Override
-    public void showWaitDialog() {
-        if (currentWaitDialog == null) {
-            currentWaitDialog = new JDialog(this, "Please wait...", true);
-            currentWaitDialog.getContentPane().add(new JLabel("Please be patient."),
-                    BorderLayout.CENTER);
-            JProgressBar progress = new JProgressBar(JProgressBar.HORIZONTAL);
-            progress.setIndeterminate(true);
-            currentWaitDialog.getContentPane().add(progress, BorderLayout.SOUTH);
-            currentWaitDialog.setSize(200, 100);
-            currentWaitDialog.setLocationRelativeTo(null);
-            showWaitDialogLater();
-        }
-    }
-
-    private void showWaitDialogLater() {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                if (currentWaitDialog != null) {
-                    currentWaitDialog.setVisible(true); // This is blocking.
-                }
-            }
-        });
-    }
-
-    @Override
-    public void updateWaitDialog(String s) {
-        if (currentWaitDialog != null) {
-            ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s);
-            Dimension prefSize = currentWaitDialog.getPreferredSize();
-            Dimension curSize = currentWaitDialog.getSize();
-            if (prefSize.width > curSize.width || prefSize.height > curSize.height) {
-                currentWaitDialog.setSize(Math.max(prefSize.width, curSize.width),
-                        Math.max(prefSize.height, curSize.height));
-                currentWaitDialog.invalidate();
-            }
-        }
-    }
-
-    @Override
-    public void hideWaitDialog() {
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-            currentWaitDialog = null;
-        }
-    }
-
-    @Override
-    public void showMessageDialog(String s) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try {
-            JOptionPane.showMessageDialog(this, s);
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    public boolean showConfirmDialog(String title, String message) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try {
-            return JOptionPane.showConfirmDialog(this, title, message, JOptionPane.YES_NO_OPTION)
-                    == JOptionPane.YES_OPTION;
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    public String showInputDialog(String message) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try {
-            return JOptionPane.showInputDialog(message);
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T> T showChoiceDialog(String title, String message, T[] choices) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try{
-            return (T)JOptionPane.showInputDialog(this,
-                    title,
-                    message,
-                    JOptionPane.QUESTION_MESSAGE,
-                    null,
-                    choices,
-                    choices[0]);
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    public File showSaveDialog() {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try{
-            if (jfc == null) {
-                jfc = new JFileChooser();
-            }
-
-            int ret = jfc.showSaveDialog(this);
-            if (ret == JFileChooser.APPROVE_OPTION) {
-                return jfc.getSelectedFile();
-            } else {
-                return null;
-            }
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    public File[] showOpenDialog(boolean multi) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try{
-            if (jfc == null) {
-                jfc = new JFileChooser();
-            }
-
-            jfc.setMultiSelectionEnabled(multi);
-            int ret = jfc.showOpenDialog(this);
-            if (ret == JFileChooser.APPROVE_OPTION) {
-                return jfc.getSelectedFiles();
-            } else {
-                return null;
-            }
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    private class ClientListCellRenderer extends DefaultListCellRenderer {
-
-        @Override
-        public Component getListCellRendererComponent(JList<?> list, Object value, int index,
-                boolean isSelected, boolean cellHasFocus) {
-            ClientData cd = ((Client) value).getClientData();
-            String s = cd.getClientDescription() + " (pid " + cd.getPid() + ")";
-            return super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
-        }
-    }
-}