Merge "Provide more information to the assistant"
diff --git a/Android.bp b/Android.bp
index 24a9bca..a740e22 100644
--- a/Android.bp
+++ b/Android.bp
@@ -132,3 +132,29 @@
     dxflags: ["--core-library"],
     installable: false,
 }
+
+python_defaults {
+    name: "base_default",
+    version: {
+        py2: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+        py3: {
+            enabled: false,
+            embedded_launcher: false,
+        },
+    },
+}
+
+python_binary_host {
+    name: "fontchain_linter",
+    defaults: ["base_default"],
+    main: "tools/fonts/fontchain_linter.py",
+    srcs: [
+        "tools/fonts/fontchain_linter.py",
+    ],
+    libs: [
+        "fontTools",
+    ],
+}
diff --git a/api/current.txt b/api/current.txt
index 06163cc..b1ca3e8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38624,7 +38624,6 @@
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
     method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
-    method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -38650,7 +38649,6 @@
     method public static int umask(int);
     method public static android.system.StructUtsname uname();
     method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
-    method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
     method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
     method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 808f222..592bd3d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -41928,7 +41928,6 @@
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
     method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
-    method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -41954,7 +41953,6 @@
     method public static int umask(int);
     method public static android.system.StructUtsname uname();
     method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
-    method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
     method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
     method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
diff --git a/api/test-current.txt b/api/test-current.txt
index da59056..98615ee 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -39015,7 +39015,6 @@
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
     method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
-    method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -39041,7 +39040,6 @@
     method public static int umask(int);
     method public static android.system.StructUtsname uname();
     method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
-    method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
     method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
     method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index b764ce5..abd2a35 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -50,8 +50,8 @@
 const int FIELD_ID_NAME = 2;
 
 StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
-                                     const std::function<void(const vector<uint8_t>&)>& pushLog)
-    : mUidMap(uidMap), mPushLog(pushLog) {
+                                     const std::function<void(const ConfigKey&)>& sendBroadcast)
+    : mUidMap(uidMap), mSendBroadcast(sendBroadcast) {
 }
 
 StatsLogProcessor::~StatsLogProcessor() {
@@ -102,12 +102,27 @@
     }
 }
 
-vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
+size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) {
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
-        return vector<uint8_t>();
+        return 0;
     }
+    return it->second->byteSize();
+}
+
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) {
+    auto it = mMetricsManagers.find(key);
+    if (it == mMetricsManagers.end()) {
+        ALOGW("Config source %s does not exist", key.ToString().c_str());
+        return;
+    }
+
+    // This allows another broadcast to be sent within the rate-limit period if we get close to
+    // filling the buffer again soon.
+    mBroadcastTimesMutex.lock();
+    mLastBroadcastTimes.erase(key);
+    mBroadcastTimesMutex.unlock();
 
     ProtoOutputStream proto;
 
@@ -131,17 +146,18 @@
     uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
     proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
 
-    vector<uint8_t> buffer(proto.size());
-    size_t pos = 0;
-    auto iter = proto.data();
-    while (iter.readBuffer() != NULL) {
-        size_t toRead = iter.currentToRead();
-        std::memcpy(&buffer[pos], iter.readBuffer(), toRead);
-        pos += toRead;
-        iter.rp()->move(toRead);
+    if (outData != nullptr) {
+        outData->clear();
+        outData->resize(proto.size());
+        size_t pos = 0;
+        auto iter = proto.data();
+        while (iter.readBuffer() != NULL) {
+            size_t toRead = iter.currentToRead();
+            std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead);
+            pos += toRead;
+            iter.rp()->move(toRead);
+        }
     }
-
-    return buffer;
 }
 
 void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
@@ -151,42 +167,34 @@
         mMetricsManagers.erase(it);
         mUidMap->OnConfigRemoved(key);
     }
-    auto flushTime = mLastFlushTimes.find(key);
-    if (flushTime != mLastFlushTimes.end()) {
-        mLastFlushTimes.erase(flushTime);
-    }
+
+    std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
+    mLastBroadcastTimes.erase(key);
 }
 
 void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs,
                                          const ConfigKey& key,
                                          const unique_ptr<MetricsManager>& metricsManager) {
-    auto lastFlushNs = mLastFlushTimes.find(key);
-    if (lastFlushNs != mLastFlushTimes.end()) {
-        if (timestampNs - lastFlushNs->second < kMinFlushPeriod) {
-            return;
-        }
-    }
+    std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
 
     size_t totalBytes = metricsManager->byteSize();
-    if (totalBytes > kMaxSerializedBytes) {
-        flush();
-        mLastFlushTimes[key] = std::move(timestampNs);
+    if (totalBytes > .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data.
+        auto lastFlushNs = mLastBroadcastTimes.find(key);
+        if (lastFlushNs != mLastBroadcastTimes.end()) {
+            if (timestampNs - lastFlushNs->second < kMinBroadcastPeriod) {
+                return;
+            }
+        }
+        mLastBroadcastTimes[key] = timestampNs;
+        ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str());
+        mSendBroadcast(key);
+    } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
+        // We ignore the return value so we force each metric producer to clear its contents.
+        metricsManager->onDumpReport();
+        ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str());
     }
 }
 
-void StatsLogProcessor::flush() {
-    // TODO: Take ConfigKey as an argument and flush metrics related to the
-    // ConfigKey. Also, create a wrapper that holds a repeated field of
-    // StatsLogReport's.
-    /*
-    StatsLogReport logReport;
-    const int numBytes = logReport.ByteSize();
-    vector<uint8_t> logReportBuffer(numBytes);
-    logReport.SerializeToArray(&logReportBuffer[0], numBytes);
-    mPushLog(logReportBuffer);
-    */
-}
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index f38d715..2091774 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -33,7 +33,7 @@
 class StatsLogProcessor : public ConfigListener {
 public:
     StatsLogProcessor(const sp<UidMap>& uidMap,
-                      const std::function<void(const vector<uint8_t>&)>& pushLog);
+                      const std::function<void(const ConfigKey&)>& sendBroadcast);
     virtual ~StatsLogProcessor();
 
     virtual void OnLogEvent(const LogEvent& event);
@@ -41,15 +41,16 @@
     void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
     void OnConfigRemoved(const ConfigKey& key);
 
-    vector<uint8_t> onDumpReport(const ConfigKey& key);
+    size_t GetMetricsSize(const ConfigKey& key);
 
-    /* Request a flush through a binder call. */
-    void flush();
+    void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData);
 
 private:
+    mutable mutex mBroadcastTimesMutex;
+
     std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers;
 
-    std::unordered_map<ConfigKey, long> mLastFlushTimes;
+    std::unordered_map<ConfigKey, long> mLastBroadcastTimes;
 
     sp<UidMap> mUidMap;  // Reference to the UidMap to lookup app name and version for each uid.
 
@@ -60,17 +61,18 @@
      */
     static const size_t kMaxSerializedBytes = 16 * 1024;
 
-    /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush
-       the logs to callback clients if true. */
+    /* Check if we should send a broadcast if approaching memory limits and if we're over, we
+     * actually delete the data. */
     void flushIfNecessary(uint64_t timestampNs,
                           const ConfigKey& key,
                           const unique_ptr<MetricsManager>& metricsManager);
 
-    std::function<void(const vector<uint8_t>&)> mPushLog;
+    // Function used to send a broadcast so that receiver for the config key can call getData
+    // to retrieve the stored data.
+    std::function<void(const ConfigKey& key)> mSendBroadcast;
 
-    /* Minimum period between two flushes in nanoseconds. Currently set to 10
-     * minutes. */
-    static const unsigned long long kMinFlushPeriod = 600 * NS_PER_SEC;
+    /* Minimum period between two broadcasts in nanoseconds. Currently set to 60 seconds. */
+    static const unsigned long long kMinBroadcastPeriod = 60 * NS_PER_SEC;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 747a571..ef01ec7 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -73,8 +73,18 @@
 {
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
-    mProcessor = new StatsLogProcessor(mUidMap, [](const vector<uint8_t>& log) {
-        // TODO: Update how we send data out of StatsD.
+    mProcessor = new StatsLogProcessor(mUidMap, [this](const ConfigKey& key) {
+        auto sc = getStatsCompanionService();
+        auto receiver = mConfigManager->GetConfigReceiver(key);
+        if (sc == nullptr) {
+            ALOGD("Could not find StatsCompanionService");
+        } else if (receiver.first.size() == 0) {
+            ALOGD("Statscompanion could not find a broadcast receiver for %s",
+                  key.ToString().c_str());
+        } else {
+            sc->sendBroadcast(String16(receiver.first.c_str()),
+                              String16(receiver.second.c_str()));
+        }
     });
 
     mConfigManager->AddListener(mProcessor);
@@ -206,7 +216,11 @@
         }
 
         if (!args[0].compare(String8("send-broadcast"))) {
-            return cmd_trigger_broadcast(args);
+            return cmd_trigger_broadcast(out, args);
+        }
+
+        if (!args[0].compare(String8("print-stats"))) {
+            return cmd_print_stats(out);
         }
 
         if (!args[0].compare(String8("clear-config"))) {
@@ -234,7 +248,7 @@
     fprintf(out, "  Removes all configs from disk.\n");
     fprintf(out, "\n");
     fprintf(out, "\n");
-    fprintf(out, "usage: adb shell cmds stats pull-source [int] \n");
+    fprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
     fprintf(out, "\n");
     fprintf(out, "  Prints the output of a pulled metrics source (int indicates source)\n");
     fprintf(out, "\n");
@@ -259,16 +273,62 @@
     fprintf(out, "  NAME          The name of the configuration\n");
     fprintf(out, "\n");
     fprintf(out, "\n");
-    fprintf(out, "usage: adb shell cmd stats send-broadcast PACKAGE CLASS\n");
-    fprintf(out, "  Send a broadcast that triggers one subscriber to fetch metrics.\n");
-    fprintf(out, "  PACKAGE        The name of the package to receive the broadcast.\n");
-    fprintf(out, "  CLASS          The name of the class to receive the broadcast.\n");
+    fprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
+    fprintf(out, "  Send a broadcast that triggers the subscriber to fetch metrics.\n");
+    fprintf(out, "  UID           The uid of the configuration. It is only possible to pass\n");
+    fprintf(out, "                the UID parameter on eng builds. If UID is omitted the\n");
+    fprintf(out, "                calling uid is used.\n");
+    fprintf(out, "  NAME          The name of the configuration\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "usage: adb shell cmd stats print-stats\n");
+    fprintf(out, "  Prints some basic stats.\n");
 }
 
-status_t StatsService::cmd_trigger_broadcast(Vector<String8>& args) {
-    auto sc = getStatsCompanionService();
-    sc->sendBroadcast(String16(args[1]), String16(args[2]));
-    ALOGD("StatsService::trigger broadcast succeeded");
+status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
+    string name;
+    bool good = false;
+    int uid;
+    const int argCount = args.size();
+    if (argCount == 2) {
+        // Automatically pick the UID
+        uid = IPCThreadState::self()->getCallingUid();
+        // TODO: What if this isn't a binder call? Should we fail?
+        name.assign(args[1].c_str(), args[1].size());
+        good = true;
+    } else if (argCount == 3) {
+        // If it's a userdebug or eng build, then the shell user can
+        // impersonate other uids.
+        if (mEngBuild) {
+            const char* s = args[1].c_str();
+            if (*s != '\0') {
+                char* end = NULL;
+                uid = strtol(s, &end, 0);
+                if (*end == '\0') {
+                    name.assign(args[2].c_str(), args[2].size());
+                    good = true;
+                }
+            }
+        } else {
+            fprintf(out,
+                    "The metrics can only be dumped for other UIDs on eng or userdebug "
+                            "builds.\n");
+        }
+    }
+    if (!good) {
+        print_cmd_help(out);
+        return UNKNOWN_ERROR;
+    }
+    auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, name));
+    sp<IStatsCompanionService> sc = getStatsCompanionService();
+    if (sc != nullptr) {
+        sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
+        ALOGD("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
+              args[2].c_str());
+    } else {
+        ALOGD("Could not access statsCompanion");
+    }
+
     return NO_ERROR;
 }
 
@@ -373,7 +433,8 @@
             }
         }
         if (good) {
-            mProcessor->onDumpReport(ConfigKey(uid, name));
+            vector<uint8_t> data;
+            mProcessor->onDumpReport(ConfigKey(uid, name), &data);
             // TODO: print the returned StatsLogReport to file instead of printing to logcat.
             fprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
             fprintf(out, "See the StatsLogReport in logcat...\n");
@@ -389,6 +450,15 @@
     }
 }
 
+status_t StatsService::cmd_print_stats(FILE* out) {
+    vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
+    for (const ConfigKey& key : configs) {
+        fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
+                mProcessor->GetMetricsSize(key));
+    }
+    return NO_ERROR;
+}
+
 status_t StatsService::cmd_print_stats_log(FILE* out, const Vector<String8>& args) {
     long msec = 0;
 
@@ -573,12 +643,14 @@
     mProcessor->OnLogEvent(event);
 }
 
-Status StatsService::getData(const String16& key, vector<uint8_t>* output) {
+Status StatsService::getData(const String16& key, vector <uint8_t>* output) {
     IPCThreadState* ipc = IPCThreadState::self();
-    if (checkCallingPermission(String16(kPermissionDump),
-                               reinterpret_cast<int32_t*>(ipc->getCallingPid()),
-                               reinterpret_cast<int32_t*>(ipc->getCallingUid()))) {
-        // TODO: Implement this.
+    ALOGD("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(),
+          ipc->getCallingUid());
+    if (checkCallingPermission(String16(kPermissionDump))) {
+        string keyStr = string(String8(key).string());
+        ConfigKey configKey(ipc->getCallingUid(), keyStr);
+        mProcessor->onDumpReport(configKey, output);
         return Status::ok();
     } else {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
@@ -590,11 +662,9 @@
                                       const String16& package, const String16& cls,
                                       bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
-    int32_t* uid = reinterpret_cast<int32_t*>(ipc->getCallingUid());
-    if (checkCallingPermission(String16(kPermissionDump),
-                               reinterpret_cast<int32_t*>(ipc->getCallingPid()), uid)) {
+    if (checkCallingPermission(String16(kPermissionDump))) {
         string keyString = string(String8(key).string());
-        ConfigKey configKey(*uid, keyString);
+        ConfigKey configKey(ipc->getCallingUid(), keyString);
         StatsdConfig cfg;
         cfg.ParseFromArray(&config[0], config.size());
         mConfigManager->UpdateConfig(configKey, cfg);
@@ -609,10 +679,9 @@
 
 Status StatsService::removeConfiguration(const String16& key, bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
-    if (checkCallingPermission(String16(kPermissionDump),
-                               reinterpret_cast<int32_t*>(ipc->getCallingPid()),
-                               reinterpret_cast<int32_t*>(ipc->getCallingUid()))) {
-        // TODO: Implement this.
+    if (checkCallingPermission(String16(kPermissionDump))) {
+        string keyStr = string(String8(key).string());
+        mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), keyStr));
         return Status::ok();
     } else {
         *success = false;
@@ -620,7 +689,7 @@
     }
 }
 
-void StatsService::binderDied(const wp<IBinder>& who) {
+void StatsService::binderDied(const wp <IBinder>& who) {
     for (size_t i = 0; i < mCallbacks.size(); i++) {
         if (IInterface::asBinder(mCallbacks[i]) == who) {
             mCallbacks.removeAt(i);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0163f94..888f97b 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -124,7 +124,7 @@
     /**
      * Trigger a broadcast.
      */
-    status_t cmd_trigger_broadcast(Vector<String8>& args);
+    status_t cmd_trigger_broadcast(FILE* out, Vector<String8>& args);
 
     /**
      * Handle the config sub-command.
@@ -132,6 +132,11 @@
     status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
 
     /**
+     * Prints some basic stats to std out.
+     */
+    status_t cmd_print_stats(FILE* out);
+
+    /**
      * Print the event log.
      */
     status_t cmd_print_stats_log(FILE* out, const Vector<String8>& args);
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 2618a21..669a4b7 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -86,6 +86,9 @@
         case LogicalOperation::NOR:
             newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue;
             break;
+        case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
+            newCondition = ConditionState::kFalse;
+            break;
     }
     return newCondition;
 }
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index bc3a7b2..2125609 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -45,7 +45,7 @@
     readConfigFromDisk();
 
     // this should be called from StatsService when it receives a statsd_config
-    UpdateConfig(ConfigKey(0, "fake"), build_fake_config());
+    UpdateConfig(ConfigKey(1000, "fake"), build_fake_config());
 }
 
 void ConfigManager::AddListener(const sp<ConfigListener>& listener) {
@@ -80,15 +80,15 @@
         // Remove from map
         mConfigs.erase(it);
 
-        // Remove from disk
-        remove_saved_configs(key);
-
         // Tell everyone
         for (auto& listener : mListeners) {
             listener->OnConfigRemoved(key);
         }
     }
-    // If we didn't find it, just quietly ignore it.
+
+    // Remove from disk. There can still be a lingering file on disk so we check
+    // whether or not the config was on memory.
+    remove_saved_configs(key);
 }
 
 void ConfigManager::remove_saved_configs(const ConfigKey& key) {
@@ -102,9 +102,7 @@
     while ((de = readdir(dir.get()))) {
         char* name = de->d_name;
         if (name[0] != '.' && strncmp(name, prefix.c_str(), prefix.size()) == 0) {
-            if (remove(StringPrintf("%s/%d-%s", STATS_SERVICE_DIR, key.GetUid(),
-                                    key.GetName().c_str())
-                               .c_str()) != 0) {
+            if (remove(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str()) != 0) {
                 ALOGD("no file found");
             }
         }
@@ -134,12 +132,34 @@
     }
 }
 
+vector<ConfigKey> ConfigManager::GetAllConfigKeys() {
+    vector<ConfigKey> ret;
+    for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) {
+        ret.push_back(it->first);
+    }
+    return ret;
+}
+
+const pair<string, string> ConfigManager::GetConfigReceiver(const ConfigKey& key) {
+    auto it = mConfigReceivers.find(key);
+    if (it == mConfigReceivers.end()) {
+        return pair<string,string>();
+    } else {
+        return it->second;
+    }
+}
+
 void ConfigManager::Dump(FILE* out) {
     fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size());
     fprintf(out, "     uid name\n");
     for (unordered_map<ConfigKey, StatsdConfig>::const_iterator it = mConfigs.begin();
          it != mConfigs.end(); it++) {
         fprintf(out, "  %6d %s\n", it->first.GetUid(), it->first.GetName().c_str());
+        auto receiverIt = mConfigReceivers.find(it->first);
+        if (receiverIt != mConfigReceivers.end()) {
+            fprintf(out, "    -> received by %s, %s\n", receiverIt->second.first.c_str(),
+                    receiverIt->second.second.c_str());
+        }
         // TODO: Print the contents of the config too.
     }
 }
@@ -190,6 +210,11 @@
 
 void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfig& config) {
     mkdir(STATS_SERVICE_DIR, S_IRWXU);
+
+    // If there is a pre-existing config with same key we should first delete it.
+    remove_saved_configs(key);
+
+    // Then we save the latest config.
     string file_name = StringPrintf("%s/%d-%s-%ld", STATS_SERVICE_DIR, key.GetUid(),
                                     key.GetName().c_str(), time(nullptr));
     int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
@@ -243,7 +268,7 @@
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
 
     // Anomaly threshold for screen-on count.
-    Alert* alert = config.add_alerts();
+    Alert* alert = config.add_alert();
     alert->set_name("1");
     alert->set_number_of_buckets(6);
     alert->set_trigger_if_sum_gt(10);
@@ -258,7 +283,7 @@
     keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
 
     // Anomaly threshold for background count.
-    alert = config.add_alerts();
+    alert = config.add_alert();
     alert->set_name("2");
     alert->set_number_of_buckets(4);
     alert->set_trigger_if_sum_gt(30);
@@ -290,7 +315,7 @@
     DurationMetric* durationMetric = config.add_duration_metric();
     durationMetric->set_name("5");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
     keyMatcher = durationMetric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
     durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
@@ -304,7 +329,7 @@
     durationMetric = config.add_duration_metric();
     durationMetric->set_name("6");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
     keyMatcher = durationMetric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
     durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
@@ -318,7 +343,7 @@
     durationMetric = config.add_duration_metric();
     durationMetric->set_name("7");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
     durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
@@ -330,7 +355,7 @@
     durationMetric = config.add_duration_metric();
     durationMetric->set_name("8");
     durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L);
-    durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
     durationMetric->set_what("SCREEN_IS_ON");
 
     // Value metric to count KERNEL_WAKELOCK when screen turned on
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 5b612cc..01d7fb9 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -68,6 +68,16 @@
     void SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls);
 
     /**
+     * Returns the package name and class name representing the broadcast receiver for this config.
+     */
+    const pair<string, string> GetConfigReceiver(const ConfigKey& key);
+
+    /**
+     * Returns all config keys registered.
+     */
+    vector<ConfigKey> GetAllConfigKeys();
+
+    /**
      * Erase any broadcast receiver associated with this config key.
      */
     void RemoveConfigReceiver(const ConfigKey& key);
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
index e004d21..e2745d2 100644
--- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -71,10 +71,9 @@
     do {
       timeMs = std::stoull(pch);
       auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ_PULLED, timestamp);
-      auto elemList = ptr->GetAndroidLogEventList();
-      *elemList << uid;
-      *elemList << idx;
-      *elemList << timeMs;
+      ptr->write(uid);
+      ptr->write(idx);
+      ptr->write(timeMs);
       ptr->init();
       data->push_back(ptr);
       VLOG("uid %lld, freq idx %d, sys time %lld", (long long)uid, idx, (long long)timeMs);
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
index b84b877..e0572dc 100644
--- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -66,10 +66,9 @@
     uint64_t sysTimeMs = std::stoull(pch);
 
     auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_PULLED, timestamp);
-    auto elemList = ptr->GetAndroidLogEventList();
-    *elemList << uid;
-    *elemList << userTimeMs;
-    *elemList << sysTimeMs;
+    ptr->write(uid);
+    ptr->write(userTimeMs);
+    ptr->write(sysTimeMs);
     ptr->init();
     data->push_back(ptr);
     VLOG("uid %lld, user time %lld, sys time %lld", (long long)uid, (long long)userTimeMs, (long long)sysTimeMs);
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
index 319feef4..3ee636d 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
@@ -93,11 +93,10 @@
 
                     auto statePtr = make_shared<LogEvent>(
                             android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, timestamp);
-                    auto elemList = statePtr->GetAndroidLogEventList();
-                    *elemList << state.name;
-                    *elemList << state.residencyInMsecSinceBoot;
-                    *elemList << state.totalTransitions;
-                    *elemList << state.supportedOnlyInSuspend;
+                    statePtr->write(state.name);
+                    statePtr->write(state.residencyInMsecSinceBoot);
+                    statePtr->write(state.totalTransitions);
+                    statePtr->write(state.supportedOnlyInSuspend);
                     statePtr->init();
                     data->push_back(statePtr);
                     VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
@@ -106,11 +105,10 @@
                     for (auto voter : state.voters) {
                         auto voterPtr =
                                 make_shared<LogEvent>(android::util::POWER_STATE_VOTER_PULLED, timestamp);
-                        auto elemList = voterPtr->GetAndroidLogEventList();
-                        *elemList << state.name;
-                        *elemList << voter.name;
-                        *elemList << voter.totalTimeInMsecVotedForSinceBoot;
-                        *elemList << voter.totalNumberOfTimesVotedSinceBoot;
+                        voterPtr->write(state.name);
+                        voterPtr->write(voter.name);
+                        voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
+                        voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
                         voterPtr->init();
                         data->push_back(voterPtr);
                         VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
@@ -141,13 +139,12 @@
                                 const PowerStateSubsystemSleepState& state = subsystem.states[j];
                                 auto subsystemStatePtr = make_shared<LogEvent>(
                                         android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, timestamp);
-                                auto elemList = subsystemStatePtr->GetAndroidLogEventList();
-                                *elemList << subsystem.name;
-                                *elemList << state.name;
-                                *elemList << state.residencyInMsecSinceBoot;
-                                *elemList << state.totalTransitions;
-                                *elemList << state.lastEntryTimestampMs;
-                                *elemList << state.supportedOnlyInSuspend;
+                                subsystemStatePtr->write(subsystem.name);
+                                subsystemStatePtr->write(state.name);
+                                subsystemStatePtr->write(state.residencyInMsecSinceBoot);
+                                subsystemStatePtr->write(state.totalTransitions);
+                                subsystemStatePtr->write(state.lastEntryTimestampMs);
+                                subsystemStatePtr->write(state.supportedOnlyInSuspend);
                                 subsystemStatePtr->init();
                                 data->push_back(subsystemStatePtr);
                                 VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 913b906..1032138 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -29,21 +29,71 @@
 using std::string;
 using android::util::ProtoOutputStream;
 
-// We need to keep a copy of the android_log_event_list owned by this instance so that the char*
-// for strings is not cleared before we can read them.
-LogEvent::LogEvent(log_msg& msg) : mList(msg) {
-    init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
+LogEvent::LogEvent(log_msg& msg) {
+    mContext =
+            create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
+    mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
+    init(mContext);
 }
 
-LogEvent::LogEvent(int tag, uint64_t timestampNs) : mList(tag), mTimestampNs(timestampNs) {
-}
-
-LogEvent::~LogEvent() {
+LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) {
+    mTimestampNs = timestampNs;
+    mTagId = tagId;
+    mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
+    if (mContext) {
+        android_log_write_int32(mContext, tagId);
+    }
 }
 
 void LogEvent::init() {
-    mList.convert_to_reader();
-    init(mTimestampNs, &mList);
+    if (mContext) {
+        const char* buffer;
+        size_t len = android_log_write_list_buffer(mContext, &buffer);
+        // turns to reader mode
+        mContext = create_android_log_parser(buffer, len);
+        init(mContext);
+    }
+}
+
+bool LogEvent::write(int32_t value) {
+    if (mContext) {
+        return android_log_write_int32(mContext, value) >= 0;
+    }
+    return false;
+}
+
+bool LogEvent::write(uint32_t value) {
+    if (mContext) {
+        return android_log_write_int32(mContext, value) >= 0;
+    }
+    return false;
+}
+
+bool LogEvent::write(uint64_t value) {
+    if (mContext) {
+        return android_log_write_int64(mContext, value) >= 0;
+    }
+    return false;
+}
+
+bool LogEvent::write(const string& value) {
+    if (mContext) {
+        return android_log_write_string8_len(mContext, value.c_str(), value.length()) >= 0;
+    }
+    return false;
+}
+
+bool LogEvent::write(float value) {
+    if (mContext) {
+        return android_log_write_float32(mContext, value) >= 0;
+    }
+    return false;
+}
+
+LogEvent::~LogEvent() {
+    if (mContext) {
+        android_log_destroy(&mContext);
+    }
 }
 
 /**
@@ -51,22 +101,25 @@
  * The goal is to do as little preprocessing as possible, because we read a tiny fraction
  * of the elements that are written to the log.
  */
-void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
-    mTimestampNs = timestampNs;
-    mTagId = reader->tag();
-
+void LogEvent::init(android_log_context context) {
     mElements.clear();
     android_log_list_element elem;
-
     // TODO: The log is actually structured inside one list.  This is convenient
     // because we'll be able to use it to put the attribution (WorkSource) block first
     // without doing our own tagging scheme.  Until that change is in, just drop the
     // list-related log elements and the order we get there is our index-keyed data
     // structure.
+    int i = 0;
     do {
-        elem = android_log_read_next(reader->context());
+        elem = android_log_read_next(context);
         switch ((int)elem.type) {
             case EVENT_TYPE_INT:
+                // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. If we add WorkSource, it would
+                // be the list starting at [2].
+                if (i == 1) {
+                    mTagId = elem.data.int32;
+                    break;
+                }
             case EVENT_TYPE_FLOAT:
             case EVENT_TYPE_STRING:
             case EVENT_TYPE_LONG:
@@ -81,13 +134,10 @@
             default:
                 break;
         }
+        i++;
     } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
 }
 
-android_log_event_list* LogEvent::GetAndroidLogEventList() {
-    return &mList;
-}
-
 int64_t LogEvent::GetLong(size_t key, status_t* err) const {
     if (key < 1 || (key - 1)  >= mElements.size()) {
         *err = BAD_INDEX;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 2984940..7e8a96b 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -21,6 +21,7 @@
 #include <android/util/ProtoOutputStream.h>
 #include <log/log_event_list.h>
 #include <log/log_read.h>
+#include <private/android_logger.h>
 #include <utils/Errors.h>
 
 #include <memory>
@@ -45,12 +46,9 @@
     explicit LogEvent(log_msg& msg);
 
     /**
-     * Constructs a LogEvent with the specified tag and creates an android_log_event_list in write
-     * mode. Obtain this list with the getter. Make sure to call init() before attempting to read
-     * any of the values. This constructor is useful for unit-testing since we can't pass in an
-     * android_log_event_list since there is no copy constructor or assignment operator available.
+     * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
      */
-    explicit LogEvent(int tag, uint64_t timestampNs);
+    explicit LogEvent(int32_t tagId, uint64_t timestampNs);
 
     ~LogEvent();
 
@@ -76,6 +74,17 @@
     float GetFloat(size_t key, status_t* err) const;
 
     /**
+     * Write test data to the LogEvent. This can only be used when the LogEvent is constructed
+     * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it.
+     */
+    bool write(uint32_t value);
+    bool write(int32_t value);
+    bool write(uint64_t value);
+    bool write(int64_t value);
+    bool write(const string& value);
+    bool write(float value);
+
+    /**
      * Return a string representation of this event.
      */
     string ToString() const;
@@ -91,13 +100,6 @@
     KeyValuePair GetKeyValueProto(size_t key) const;
 
     /**
-     * A pointer to the contained log_event_list.
-     *
-     * @return The android_log_event_list contained within.
-     */
-    android_log_event_list* GetAndroidLogEventList();
-
-    /**
      * Used with the constructor where tag is passed in. Converts the log_event_list to read mode
      * and prepares the list for reading.
      */
@@ -113,16 +115,11 @@
     /**
      * Parses a log_msg into a LogEvent object.
      */
-    void init(const log_msg& msg);
-
-    /**
-     * Parses a log_msg into a LogEvent object.
-     */
-    void init(int64_t timestampNs, android_log_event_list* reader);
+    void init(android_log_context context);
 
     vector<android_log_list_element> mElements;
-    // Need a copy of the android_log_event_list so the strings are not cleared.
-    android_log_event_list mList;
+
+    android_log_context mContext;
 
     uint64_t mTimestampNs;
 
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 1c699e8..f7352cd 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -84,6 +84,9 @@
                 }
             }
             break;
+        case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
+            matched = false;
+            break;
     }
     return matched;
 }
@@ -165,15 +168,15 @@
                    matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
             // Float fields
             status_t err = NO_ERROR;
-            bool val = event.GetFloat(key, &err);
+            float val = event.GetFloat(key, &err);
             if (err == NO_ERROR) {
                 if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat) {
-                    if (!(cur.lt_float() <= val)) {
+                    if (!(val < cur.lt_float())) {
                         allMatched = false;
                         break;
                     }
                 } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
-                    if (!(cur.gt_float() >= val)) {
+                    if (!(val > cur.gt_float())) {
                         allMatched = false;
                         break;
                     }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index de6f365..eba2e06 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -63,6 +63,7 @@
 DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
                                                const int conditionIndex, const size_t startIndex,
                                                const size_t stopIndex, const size_t stopAllIndex,
+                                               const bool nesting,
                                                const sp<ConditionWizard>& wizard,
                                                const vector<KeyMatcher>& internalDimension,
                                                const uint64_t startTimeNs)
@@ -71,6 +72,7 @@
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
       mStopAllIndex(stopAllIndex),
+      mNested(nesting),
       mInternalDimension(internalDimension) {
     // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
     // them in the base class, because the proto generated CountMetric, and DurationMetric are
@@ -109,13 +111,13 @@
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
         vector<DurationBucket>& bucket) {
-    switch (mMetric.type()) {
-        case DurationMetric_AggregationType_DURATION_SUM:
-            return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex,
+    switch (mMetric.aggregation_type()) {
+        case DurationMetric_AggregationType_SUM:
+            return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex, mNested,
                                                      mCurrentBucketStartTimeNs, mBucketSizeNs,
                                                      bucket);
-        case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
-            return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex,
+        case DurationMetric_AggregationType_MAX_SPARSE:
+            return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex, mNested,
                                                    mCurrentBucketStartTimeNs, mBucketSizeNs,
                                                    bucket);
     }
@@ -220,10 +222,8 @@
                   (long long)mCurrentBucketStartTimeNs);
 
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
-
     startNewProtoOutputStream(endTime);
-    mPastBuckets.clear();
-
+    // TODO: Properly clear the old buckets.
     return buffer;
 }
 
@@ -270,7 +270,7 @@
     if (matcherIndex == mStartIndex) {
         it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
     } else if (matcherIndex == mStopIndex) {
-        it->second->noteStop(atomKey, event.GetTimestampNs());
+        it->second->noteStop(atomKey, event.GetTimestampNs(), false);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index eea00454..bb5d4d9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -39,7 +39,8 @@
 public:
     DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
                            const size_t startIndex, const size_t stopIndex,
-                           const size_t stopAllIndex, const sp<ConditionWizard>& wizard,
+                           const size_t stopAllIndex, const bool nesting,
+                           const sp<ConditionWizard>& wizard,
                            const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
 
     virtual ~DurationMetricProducer();
@@ -80,6 +81,9 @@
     // Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions.
     const size_t mStopAllIndex;
 
+    // nest counting -- for the same key, stops must match the number of starts to make real stop
+    const bool mNested;
+
     // The dimension from the atom predicate. e.g., uid, wakelock name.
     const vector<KeyMatcher> mInternalDimension;
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 74ba40b..9a94a0e 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -96,7 +96,6 @@
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(endTime);
-    mByteSize = 0;
 
     return buffer;
 }
@@ -121,11 +120,10 @@
     event.ToProto(*mProto);
     mProto->end(eventToken);
     mProto->end(wrapperToken);
-    // TODO: Find a proper way to derive the size of incoming LogEvent.
 }
 
 size_t EventMetricProducer::byteSize() {
-    return mByteSize;
+    return mProto->bytesWritten();
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 7740621..0dccdf4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -65,8 +65,6 @@
 
 private:
     const EventMetric mMetric;
-
-    size_t mByteSize;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index c0930e3..c7982a8 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -61,12 +61,15 @@
 
     // TODO: Pass a timestamp as a parameter in onDumpReport and update all its
     // implementations.
+    // onDumpReport returns the proto-serialized output and clears the previously stored contents.
     virtual std::unique_ptr<std::vector<uint8_t>> onDumpReport() = 0;
 
     virtual bool isConditionSliced() const {
         return mConditionSliced;
     };
 
+    // Returns the memory in bytes currently used to store this metric's data. Does not change
+    // state.
     virtual size_t byteSize() = 0;
 
 protected:
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 39c79f9..fb16779 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -46,6 +46,8 @@
     // Config source owner can call onDumpReport() to get all the metrics collected.
     std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
 
+    // Computes the total byte size of all metrics managed by a single config source.
+    // Does not change the state.
     size_t byteSize();
 
 private:
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 5c76d0e..18b3349 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -34,6 +34,10 @@
 // Hold duration information for one atom level duration in current on-going bucket.
 struct DurationInfo {
     DurationState state;
+
+    // the number of starts seen.
+    int32_t startCount;
+
     // most recent start time.
     int64_t lastStartTime;
     // existing duration in current bucket.
@@ -42,7 +46,7 @@
     // cache the HashableDimensionKeys we need to query the condition for this duration event.
     ConditionKey conditionKeys;
 
-    DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
+    DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
 };
 
 struct DurationBucket {
@@ -53,18 +57,21 @@
 
 class DurationTracker {
 public:
-    DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, uint64_t currentBucketStartNs,
-                    uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket)
+    DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+                    uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                    std::vector<DurationBucket>& bucket)
         : mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
-          mCurrentBucketStartTimeNs(currentBucketStartNs),
           mBucketSizeNs(bucketSizeNs),
+          mNested(nesting),
+          mCurrentBucketStartTimeNs(currentBucketStartNs),
           mBucket(bucket),
           mDuration(0){};
     virtual ~DurationTracker(){};
     virtual void noteStart(const HashableDimensionKey& key, bool condition,
                            const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
-    virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) = 0;
+    virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+                          const bool stopAll) = 0;
     virtual void noteStopAll(const uint64_t eventTime) = 0;
     virtual void onSlicedConditionMayChange(const uint64_t timestamp) = 0;
     virtual void onConditionChanged(bool condition, const uint64_t timestamp) = 0;
@@ -75,12 +82,14 @@
 protected:
     sp<ConditionWizard> mWizard;
 
-    int mConditionTrackerIndex;
+    const int mConditionTrackerIndex;
+
+    const int64_t mBucketSizeNs;
+
+    const bool mNested;
 
     uint64_t mCurrentBucketStartTimeNs;
 
-    int64_t mBucketSizeNs;
-
     std::vector<DurationBucket>& mBucket;  // where to write output
 
     int64_t mDuration;  // current recorded duration result
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 43c21a8..8c7bfb6 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -23,10 +23,10 @@
 namespace os {
 namespace statsd {
 
-MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                                        std::vector<DurationBucket>& bucket)
-    : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket) {
+    : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket) {
 }
 
 void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
@@ -38,10 +38,10 @@
 
     switch (duration.state) {
         case kStarted:
-            // The same event is already started. Because we are not counting nesting, so ignore.
+            duration.startCount++;
             break;
         case kPaused:
-            // Safe to do nothing here. Paused means started but condition is false.
+            duration.startCount++;
             break;
         case kStopped:
             if (!condition) {
@@ -51,11 +51,13 @@
                 duration.state = DurationState::kStarted;
                 duration.lastStartTime = eventTime;
             }
+            duration.startCount = 1;
             break;
     }
 }
 
-void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
+void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+                                  bool forceStop) {
     VLOG("MaxDuration: key %s stop", key.c_str());
     if (mInfos.find(key) == mInfos.end()) {
         // we didn't see a start event before. do nothing.
@@ -68,16 +70,23 @@
             // already stopped, do nothing.
             break;
         case DurationState::kStarted: {
-            duration.state = DurationState::kStopped;
-            int64_t durationTime = eventTime - duration.lastStartTime;
-            VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), (long long)duration.lastStartTime,
-                 (long long)eventTime, (long long)durationTime);
-            duration.lastDuration = duration.lastDuration + durationTime;
-            VLOG("  record duration: %lld ", (long long)duration.lastDuration);
+            duration.startCount--;
+            if (forceStop || !mNested || duration.startCount <= 0) {
+                duration.state = DurationState::kStopped;
+                int64_t durationTime = eventTime - duration.lastStartTime;
+                VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(),
+                     (long long)duration.lastStartTime, (long long)eventTime,
+                     (long long)durationTime);
+                duration.lastDuration = duration.lastDuration + durationTime;
+                VLOG("  record duration: %lld ", (long long)duration.lastDuration);
+            }
             break;
         }
         case DurationState::kPaused: {
-            duration.state = DurationState::kStopped;
+            duration.startCount--;
+            if (forceStop || !mNested || duration.startCount <= 0) {
+                duration.state = DurationState::kStopped;
+            }
             break;
         }
     }
@@ -88,11 +97,13 @@
     }
     // Once an atom duration ends, we erase it. Next time, if we see another atom event with the
     // same name, they are still considered as different atom durations.
-    mInfos.erase(key);
+    if (duration.state == DurationState::kStopped) {
+        mInfos.erase(key);
+    }
 }
 void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
     for (auto& pair : mInfos) {
-        noteStop(pair.first, eventTime);
+        noteStop(pair.first, eventTime, true);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index b095884..167f81e 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,12 +28,13 @@
 // they stop or bucket expires.
 class MaxDurationTracker : public DurationTracker {
 public:
-    MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+    MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                        std::vector<DurationBucket>& bucket);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
-    void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+    void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+                  const bool stopAll) override;
     void noteStopAll(const uint64_t eventTime) override;
     bool flushIfNeeded(uint64_t timestampNs) override;
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index e4f1d21..faf5ce5 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -20,10 +20,14 @@
 namespace android {
 namespace os {
 namespace statsd {
+
+using std::pair;
+
 OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
-                                           uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                                           bool nesting, uint64_t currentBucketStartNs,
+                                           uint64_t bucketSizeNs,
                                            std::vector<DurationBucket>& bucket)
-    : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket),
+    : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
@@ -36,9 +40,9 @@
             mLastStartTime = eventTime;
             VLOG("record first start....");
         }
-        mStarted.insert(key);
+        mStarted[key]++;
     } else {
-        mPaused.insert(key);
+        mPaused[key]++;
     }
 
     if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
@@ -48,11 +52,16 @@
     VLOG("Oring: %s start, condition %d", key.c_str(), condition);
 }
 
-void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp) {
+void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp,
+                                    const bool stopAll) {
     VLOG("Oring: %s stop", key.c_str());
     auto it = mStarted.find(key);
     if (it != mStarted.end()) {
-        mStarted.erase(it);
+        (it->second)--;
+        if (stopAll || !mNested || it->second <= 0) {
+            mStarted.erase(it);
+            mConditionKeyMap.erase(key);
+        }
         if (mStarted.empty()) {
             mDuration += (timestamp - mLastStartTime);
             VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
@@ -60,8 +69,14 @@
         }
     }
 
-    mPaused.erase(key);
-    mConditionKeyMap.erase(key);
+    auto pausedIt = mPaused.find(key);
+    if (pausedIt != mPaused.end()) {
+        (pausedIt->second)--;
+        if (stopAll || !mNested || pausedIt->second <= 0) {
+            mPaused.erase(pausedIt);
+            mConditionKeyMap.erase(key);
+        }
+        }
 }
 void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
     if (!mStarted.empty()) {
@@ -118,11 +133,11 @@
 }
 
 void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
-    vector<HashableDimensionKey> startedToPaused;
-    vector<HashableDimensionKey> pausedToStarted;
+    vector<pair<HashableDimensionKey, int>> startedToPaused;
+    vector<pair<HashableDimensionKey, int>> pausedToStarted;
     if (!mStarted.empty()) {
         for (auto it = mStarted.begin(); it != mStarted.end();) {
-            auto key = *it;
+            const auto& key = it->first;
             if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
                 VLOG("Key %s dont have condition key", key.c_str());
                 ++it;
@@ -130,8 +145,8 @@
             }
             if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
                 ConditionState::kTrue) {
+                startedToPaused.push_back(*it);
                 it = mStarted.erase(it);
-                startedToPaused.push_back(key);
                 VLOG("Key %s started -> paused", key.c_str());
             } else {
                 ++it;
@@ -147,7 +162,7 @@
 
     if (!mPaused.empty()) {
         for (auto it = mPaused.begin(); it != mPaused.end();) {
-            auto key = *it;
+            const auto& key = it->first;
             if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
                 VLOG("Key %s dont have condition key", key.c_str());
                 ++it;
@@ -155,8 +170,8 @@
             }
             if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
                 ConditionState::kTrue) {
+                pausedToStarted.push_back(*it);
                 it = mPaused.erase(it);
-                pausedToStarted.push_back(key);
                 VLOG("Key %s paused -> started", key.c_str());
             } else {
                 ++it;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index b54dafa..78760ba 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,12 +27,13 @@
 // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
 class OringDurationTracker : public DurationTracker {
 public:
-    OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+    OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                          uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                          std::vector<DurationBucket>& bucket);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
-    void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+    void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+                  const bool stopAll) override;
     void noteStopAll(const uint64_t eventTime) override;
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
@@ -44,8 +45,8 @@
     // 2) which keys are paused (started but condition was false)
     // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty,
     //    it means everything has stopped, we then record the end time.
-    std::set<HashableDimensionKey> mStarted;
-    std::set<HashableDimensionKey> mPaused;
+    std::map<HashableDimensionKey, int> mStarted;
+    std::map<HashableDimensionKey, int> mPaused;
     int64_t mLastStartTime;
     std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
 };
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4c63b20..d83c144 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -252,6 +252,8 @@
 
         const auto& simpleCondition = durationWhat.simple_condition();
 
+        bool nesting = simpleCondition.count_nesting();
+
         int trackerIndices[3] = {-1, -1, -1};
         if (!simpleCondition.has_start() ||
             !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
@@ -294,7 +296,7 @@
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
                 metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
-                wizard, internalDimension, startTimeNs);
+                nesting, wizard, internalDimension, startTimeNs);
 
         allMetricProducers.push_back(durationMetric);
     }
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 9a760b1..d3b04ba 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -47,6 +47,7 @@
 }
 
 enum LogicalOperation {
+  LOGICAL_OPERATION_UNSPECIFIED = 0;
   AND = 1;
   OR = 2;
   NOT = 3;
@@ -111,23 +112,12 @@
   optional int64 bucket_size_millis = 1;
 }
 
-message Alert {
-    optional string name = 1;
+message EventConditionLink {
+  optional string condition = 1;
 
-    optional string metric_name = 2;
+  repeated KeyMatcher key_in_main = 2;
 
-    message IncidentdDetails {
-        repeated int32 section = 1;
-  }
-  optional IncidentdDetails incidentd_details = 3;
-
-  optional int32 number_of_buckets = 4;
-
-  optional int32 refractory_period_secs = 5;
-
-  optional int64 trigger_if_sum_gt = 6;
-
-  optional int32 refractory_period_in_buckets = 7;
+  repeated KeyMatcher key_in_condition = 3;
 }
 
 message EventMetric {
@@ -151,9 +141,7 @@
 
     optional Bucket bucket = 5;
 
-    optional bool include_in_output = 6;
-
-    repeated EventConditionLink links = 7;
+    repeated EventConditionLink links = 6;
 }
 
 message DurationMetric {
@@ -166,11 +154,11 @@
     repeated EventConditionLink links = 4;
 
     enum AggregationType {
-        DURATION_SUM = 1;
+      SUM = 1;
 
-        DURATION_MAX_SPARSE = 2;
+      MAX_SPARSE = 2;
     }
-    optional AggregationType type = 5;
+    optional AggregationType aggregation_type = 5 [default = SUM];
 
     repeated KeyMatcher dimension = 6;
 
@@ -208,16 +196,30 @@
 
     repeated EventConditionLink links = 7;
 
-    enum Operation { SUM = 1; }
-    optional Operation operation = 9 [default = SUM];
+    enum AggregationType {
+      SUM = 1;
+    }
+    optional AggregationType aggregation_type = 8 [default = SUM];
 }
 
-message EventConditionLink {
-  optional string condition = 1;
+message Alert {
+    optional string name = 1;
 
-  repeated KeyMatcher key_in_main = 2;
-  repeated KeyMatcher key_in_condition = 3;
-};
+    optional string metric_name = 2;
+
+    message IncidentdDetails {
+      repeated int32 section = 1;
+    }
+    optional IncidentdDetails incidentd_details = 3;
+
+    optional int32 number_of_buckets = 4;
+
+    optional int32 refractory_period_secs = 5;
+
+    optional int64 trigger_if_sum_gt = 6;
+
+    optional int32 refractory_period_in_buckets = 7;
+}
 
 message StatsdConfig {
     optional string name = 1;
@@ -236,5 +238,5 @@
 
     repeated Condition condition = 8;
 
-    repeated Alert alerts = 9;
+    repeated Alert alert = 9;
 }
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index fad5de6..f570522 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -85,7 +85,7 @@
         // TODO: Remove this when we get rid of the fake one, and make this
         // test loading one from disk somewhere.
         EXPECT_CALL(*(listener.get()),
-                    OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq("12345")))
+                    OnConfigUpdated(ConfigKeyEq(1000, "fake"), StatsdConfigEq("12345")))
                 .RetiresOnSaturation();
         manager->Startup();
 
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index bb4930a..40c0e9d 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -27,7 +27,7 @@
 using std::unordered_map;
 using std::vector;
 
-const int TAG_ID = 123;
+const int32_t TAG_ID = 123;
 const int FIELD_ID_1 = 1;
 const int FIELD_ID_2 = 2;
 const int FIELD_ID_3 = 2;
@@ -43,8 +43,6 @@
     simpleMatcher->set_tag(TAG_ID);
 
     LogEvent event(TAG_ID, 0);
-
-    // Convert to a LogEvent
     event.init();
 
     // Test
@@ -63,10 +61,8 @@
 
     // Set up the event
     LogEvent event(TAG_ID, 0);
-    auto list = event.GetAndroidLogEventList();
-    *list << true;
-    *list << false;
-
+    event.write(true);
+    event.write(false);
     // Convert to a LogEvent
     event.init();
 
@@ -99,9 +95,7 @@
 
     // Set up the event
     LogEvent event(TAG_ID, 0);
-    auto list = event.GetAndroidLogEventList();
-    *list << "some value";
-
+    event.write("some value");
     // Convert to a LogEvent
     event.init();
 
@@ -121,9 +115,8 @@
 
     // Set up the event
     LogEvent event(TAG_ID, 0);
-    auto list = event.GetAndroidLogEventList();
-    *list << 2;
-    *list << 3;
+    event.write(2);
+    event.write(3);
 
     // Convert to a LogEvent
     event.init();
@@ -153,9 +146,7 @@
 
     // Set up the event
     LogEvent event(TAG_ID, 0);
-    auto list = event.GetAndroidLogEventList();
-    *list << 11;
-
+    event.write(11);
     event.init();
 
     // Test
@@ -201,8 +192,6 @@
     EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
 }
 
-#if 0
-
 TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
     // Set up the matcher
     LogEntryMatcher matcher;
@@ -212,22 +201,28 @@
     auto keyValue = simpleMatcher->add_key_value_matcher();
     keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
 
-    LogEvent event;
-    event.tagId = TAG_ID;
-
+    LogEvent event1(TAG_ID, 0);
     keyValue->set_lt_float(10.0);
-    event.floatMap[FIELD_ID_1] = 10.1;
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
-    event.floatMap[FIELD_ID_1] = 9.9;
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    event1.write(10.1f);
+    event1.init();
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, event1));
 
+    LogEvent event2(TAG_ID, 0);
+    event2.write(9.9f);
+    event2.init();
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, event2));
+
+    LogEvent event3(TAG_ID, 0);
+    event3.write(10.1f);
+    event3.init();
     keyValue->set_gt_float(10.0);
-    event.floatMap[FIELD_ID_1] = 10.1;
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
-    event.floatMap[FIELD_ID_1] = 9.9;
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(*simpleMatcher, event3));
+
+    LogEvent event4(TAG_ID, 0);
+    event4.write(9.9f);
+    event4.init();
+    EXPECT_FALSE(matchesSimple(*simpleMatcher, event4));
 }
-#endif
 
 // Helper for the composite matchers.
 void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, int tag, int key, int val) {
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index c64719e..4c12b03 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -36,10 +36,9 @@
     sp<UidMap> m = new UidMap();
     StatsLogProcessor p(m, nullptr);
     LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
-    android_log_event_list* list = addEvent.GetAndroidLogEventList();
-    *list << 100;  // parent UID
-    *list << 101;  // isolated UID
-    *list << 1;    // Indicates creation.
+    addEvent.write(100);  // parent UID
+    addEvent.write(101);  // isolated UID
+    addEvent.write(1);    // Indicates creation.
     addEvent.init();
 
     EXPECT_EQ(101, m->getParentUidOrSelf(101));
@@ -48,10 +47,9 @@
     EXPECT_EQ(100, m->getParentUidOrSelf(101));
 
     LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1);
-    list = removeEvent.GetAndroidLogEventList();
-    *list << 100;  // parent UID
-    *list << 101;  // isolated UID
-    *list << 0;    // Indicates removal.
+    removeEvent.write(100);  // parent UID
+    removeEvent.write(101);  // isolated UID
+    removeEvent.write(0);    // Indicates removal.
     removeEvent.init();
     p.OnLogEvent(removeEvent);
     EXPECT_EQ(101, m->getParentUidOrSelf(101));
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 855e666..80a0068 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -46,10 +46,9 @@
 }
 
 void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
-    auto list = event->GetAndroidLogEventList();
-    *list << uid;  // uid
-    *list << wl;
-    *list << acquire;
+    event->write(uid);  // uid
+    event->write(wl);
+    event->write(acquire);
     event->init();
 }
 
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d07a84d..b7c9b40 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -138,15 +138,13 @@
     link->add_key_in_condition()->set_key(2);
 
     LogEvent event1(1, bucketStartTimeNs + 1);
-    auto list = event1.GetAndroidLogEventList();
-    *list << "111";  // uid
+    event1.write("111");  // uid
     event1.init();
     ConditionKey key1;
     key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
 
     LogEvent event2(1, bucketStartTimeNs + 10);
-    auto list2 = event2.GetAndroidLogEventList();
-    *list2 << "222";  // uid
+    event2.write("222");  // uid
     event2.init();
     ConditionKey key2;
     key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 0971d26..18d177c 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -95,15 +95,13 @@
     link->add_key_in_condition()->set_key(2);
 
     LogEvent event1(1, bucketStartTimeNs + 1);
-    auto list = event1.GetAndroidLogEventList();
-    *list << "111";  // uid
+    event1.write("111");  // uid
     event1.init();
     ConditionKey key1;
     key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
 
     LogEvent event2(1, bucketStartTimeNs + 10);
-    auto list2 = event2.GetAndroidLogEventList();
-    *list2 << "222";  // uid
+    event2.write("222");  // uid
     event2.init();
     ConditionKey key2;
     key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 58bf1b3..9cc184a 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -45,13 +45,13 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("", true, bucketStartTimeNs, key1);
-    tracker.noteStop("", bucketStartTimeNs + 10);
+    tracker.noteStop("", bucketStartTimeNs + 10, false);
 
     tracker.noteStart("", true, bucketStartTimeNs + 20, key1);
-    tracker.noteStop("", bucketStartTimeNs + 40);
+    tracker.noteStop("", bucketStartTimeNs + 40, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
@@ -67,7 +67,7 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
     tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
@@ -77,6 +77,37 @@
     EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
 }
 
+TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    vector<DurationBucket> buckets;
+    ConditionKey key1;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    MaxDurationTracker tracker(wizard, -1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+    // 2 starts
+    tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+    tracker.noteStart("", true, bucketStartTimeNs + 10, key1);
+    // one stop
+    tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
+
+    EXPECT_EQ(2u, buckets.size());
+    EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].mDuration);
+    EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
+
+    // real stop now.
+    tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1);
+
+    EXPECT_EQ(3u, buckets.size());
+    EXPECT_EQ(5, buckets[2].mDuration);
+}
+
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -93,13 +124,13 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    MaxDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+    MaxDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 74a6f11..f495d6b 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -47,18 +47,43 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+    OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
     EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
 }
 
+TEST(OringDurationTrackerTest, TestDurationNested) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+
+    vector<DurationBucket> buckets;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
+
+    tracker.noteStop("2:maps", eventStartTimeNs + 2000, false);
+    tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(2003, buckets[0].mDuration);
+}
+
 TEST(OringDurationTrackerTest, TestDurationConditionChange) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -75,18 +100,50 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+    OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1u, buckets.size());
     EXPECT_EQ(5, buckets[0].mDuration);
 }
+
+TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+
+    EXPECT_CALL(*wizard, query(_, key1))  // #4
+            .WillOnce(Return(ConditionState::kFalse));
+
+    vector<DurationBucket> buckets;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
+
+    tracker.noteStop("2:maps", eventStartTimeNs + 3, false);
+
+    tracker.onSlicedConditionMayChange(eventStartTimeNs + 15);
+
+    tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(15, buckets[0].mDuration);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 72b4194..cd647cb 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -64,9 +64,8 @@
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
-    auto list = event->GetAndroidLogEventList();
-    *list << 1;
-    *list << 11;
+    event->write(1);
+    event->write(11);
     event->init();
     allData.push_back(event);
 
@@ -89,9 +88,8 @@
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-    list = event->GetAndroidLogEventList();
-    *list << 1;
-    *list << 22;
+    event->write(1);
+    event->write(22);
     event->init();
     allData.push_back(event);
     valueProducer.onDataPulled(allData);
@@ -110,9 +108,8 @@
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-    list = event->GetAndroidLogEventList();
-    *list << 1;
-    *list << 33;
+    event->write(1);
+    event->write(33);
     event->init();
     allData.push_back(event);
     valueProducer.onDataPulled(allData);
@@ -159,9 +156,8 @@
         int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
         data->clear();
         shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-        auto list = event->GetAndroidLogEventList();
-        *list << 1;
-        *list << 100;
+        event->write(1);
+        event->write(100);
         event->init();
         data->push_back(event);
         return true;
@@ -174,9 +170,8 @@
         int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
         data->clear();
         shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-        auto list = event->GetAndroidLogEventList();
-        *list << 1;
-        *list << 120;
+        event->write(1);
+        event->write(120);
         event->init();
         data->push_back(event);
         return true;
@@ -201,9 +196,8 @@
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-    auto list = event->GetAndroidLogEventList();
-    *list << 1;
-    *list << 110;
+    event->write(1);
+    event->write(110);
     event->init();
     allData.push_back(event);
     valueProducer.onDataPulled(allData);
@@ -253,14 +247,12 @@
                                       bucketStartTimeNs, pullerManager);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-    auto list = event1->GetAndroidLogEventList();
-    *list << 1;
-    *list << 10;
+    event1->write(1);
+    event1->write(10);
     event1->init();
     shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-    auto list2 = event2->GetAndroidLogEventList();
-    *list2 << 1;
-    *list2 << 20;
+    event2->write(1);
+    event2->write(20);
     event2->init();
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1, false);
     // has one slice
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 99f3dee..03a3631 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4379,7 +4379,7 @@
             throw new IllegalArgumentException("requestCode should be >= 0");
         }
         if (mHasCurrentPermissionsRequest) {
-            Log.w(TAG, "Can reqeust only one set of permissions at a time");
+            Log.w(TAG, "Can request only one set of permissions at a time");
             // Dispatch the callback with empty arrays which means a cancellation.
             onRequestPermissionsResult(requestCode, new String[0], new int[0]);
             return;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 21e454f..95c5fb5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5380,7 +5380,7 @@
             }
         }
 
-        GraphicsEnvironment.chooseDriver(context);
+        GraphicsEnvironment.getInstance().setup(context);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index b7c1f4e..7257044 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -17,9 +17,12 @@
 package android.app;
 
 import android.os.Build;
+import android.os.GraphicsEnvironment;
 import android.os.Trace;
 import android.util.ArrayMap;
+
 import com.android.internal.os.ClassLoaderFactory;
+
 import dalvik.system.PathClassLoader;
 
 /** @hide */
@@ -72,8 +75,9 @@
 
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
-                setupVulkanLayerPath(classloader, librarySearchPath);
+                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setLayerPaths");
+                GraphicsEnvironment.getInstance().setLayerPaths(
+                        classloader, librarySearchPath, libraryPermittedPath);
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
                 mLoaders.put(cacheKey, classloader);
@@ -105,8 +109,6 @@
                               cacheKey, null /* classLoaderName */);
     }
 
-    private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
-
     /**
      * Adds a new path the classpath of the given loader.
      * @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d5d95fb..42c1347 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -68,6 +68,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
 import android.view.NotificationHeaderView;
 import android.view.View;
@@ -2447,6 +2448,30 @@
         notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
     }
 
+    /**
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(NotificationProto.CHANNEL_ID, getChannelId());
+        proto.write(NotificationProto.HAS_TICKER_TEXT, this.tickerText != null);
+        proto.write(NotificationProto.FLAGS, this.flags);
+        proto.write(NotificationProto.COLOR, this.color);
+        proto.write(NotificationProto.CATEGORY, this.category);
+        proto.write(NotificationProto.GROUP_KEY, this.mGroupKey);
+        proto.write(NotificationProto.SORT_KEY, this.mSortKey);
+        if (this.actions != null) {
+            proto.write(NotificationProto.ACTION_LENGTH, this.actions.length);
+        }
+        if (this.visibility >= VISIBILITY_SECRET && this.visibility <= VISIBILITY_PUBLIC) {
+            proto.write(NotificationProto.VISIBILITY, this.visibility);
+        }
+        if (publicVersion != null) {
+            publicVersion.writeToProto(proto, NotificationProto.PUBLIC_VERSION);
+        }
+        proto.end(token);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0d7a941..8200414 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -100,6 +100,12 @@
          */
         public static final int CHOOSER_ACTION = 9;
 
+        /**
+         * An event type denoting that a notification was viewed by the user.
+         * @hide
+         */
+        public static final int NOTIFICATION_SEEN = 10;
+
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1352bc2..64d33d5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -543,9 +543,10 @@
     void forceDexOpt(String packageName);
 
     /**
-     * Execute the background dexopt job immediately.
+     * Execute the background dexopt job immediately on packages in packageNames.
+     * If null, then execute on all packages.
      */
-    boolean runBackgroundDexoptJob();
+    boolean runBackgroundDexoptJob(in List<String> packageNames);
 
     /**
      * Reconcile the information we have about the secondary dex files belonging to
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 86288396..77c5743 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -81,6 +81,9 @@
  * <li>All APKs must have unique split names.
  * <li>All installations must contain a single base APK.
  * </ul>
+ * <p>
+ * The ApiDemos project contains examples of using this API:
+ * <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>.
  */
 public class PackageInstaller {
     private static final String TAG = "PackageInstaller";
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 07c6055..f2e0bdd 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -22,6 +22,7 @@
 import android.opengl.EGL14;
 import android.os.Build;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Log;
 
 import dalvik.system.VMRuntime;
@@ -29,18 +30,110 @@
 import java.io.File;
 
 /** @hide */
-public final class GraphicsEnvironment {
+public class GraphicsEnvironment {
+
+    private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
+
+    /**
+     * Returns the shared {@link GraphicsEnvironment} instance.
+     */
+    public static GraphicsEnvironment getInstance() {
+        return sInstance;
+    }
 
     private static final boolean DEBUG = false;
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
 
+    private ClassLoader mClassLoader;
+    private String mLayerPath;
+    private String mDebugLayerPath;
+
+    /**
+     * Set up GraphicsEnvironment
+     */
+    public void setup(Context context) {
+        setupGpuLayers(context);
+        chooseDriver(context);
+    }
+
+    /**
+     * Check whether application is debuggable
+     */
+    private static boolean isDebuggable(Context context) {
+        return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0;
+    }
+
+    /**
+     * Store the layer paths available to the loader.
+     */
+    public void setLayerPaths(ClassLoader classLoader,
+                              String layerPath,
+                              String debugLayerPath) {
+        // We have to store these in the class because they are set up before we
+        // have access to the Context to properly set up GraphicsEnvironment
+        mClassLoader = classLoader;
+        mLayerPath = layerPath;
+        mDebugLayerPath = debugLayerPath;
+    }
+
+    /**
+     * Set up layer search paths for all apps
+     * If debuggable, check for additional debug settings
+     */
+    private void setupGpuLayers(Context context) {
+
+        String layerPaths = "";
+
+        // Only enable additional debug functionality if the following conditions are met:
+        // 1. App is debuggable
+        // 2. ENABLE_GPU_DEBUG_LAYERS is true
+        // 3. Package name is equal to GPU_DEBUG_APP
+
+        if (isDebuggable(context)) {
+
+            int enable = Settings.Global.getInt(context.getContentResolver(),
+                                                Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+
+            if (enable != 0) {
+
+                String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
+                                                               Settings.Global.GPU_DEBUG_APP);
+
+                String packageName = context.getPackageName();
+
+                if ((gpuDebugApp != null && packageName != null)
+                        && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
+                        && gpuDebugApp.equals(packageName)) {
+                    Log.i(TAG, "GPU debug layers enabled for " + packageName);
+
+                    // Prepend the debug layer path as a searchable path.
+                    // This will ensure debug layers added will take precedence over
+                    // the layers specified by the app.
+                    layerPaths = mDebugLayerPath + ":";
+
+                    String layers = Settings.Global.getString(context.getContentResolver(),
+                                                              Settings.Global.GPU_DEBUG_LAYERS);
+
+                    Log.i(TAG, "Debug layer list: " + layers);
+                    if (layers != null && !layers.isEmpty()) {
+                        setDebugLayers(layers);
+                    }
+                }
+            }
+
+        }
+
+        // Include the app's lib directory in all cases
+        layerPaths += mLayerPath;
+
+        setLayerPaths(mClassLoader, layerPaths);
+    }
+
     /**
      * Choose whether the current process should use the builtin or an updated driver.
-     *
-     * @hide
      */
-    public static void chooseDriver(Context context) {
+    private static void chooseDriver(Context context) {
         String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
         if (driverPackageName == null || driverPackageName.isEmpty()) {
             return;
@@ -99,8 +192,6 @@
      * on a separate thread, it can usually be finished well before the UI is ready to be drawn.
      *
      * Should only be called after chooseDriver().
-     *
-     * @hide
      */
     public static void earlyInitEGL() {
         Thread eglInitThread = new Thread(
@@ -124,6 +215,7 @@
         return null;
     }
 
+    private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
+    private static native void setDebugLayers(String layers);
     private static native void setDriverPath(String path);
-
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6decc30..9945755 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9854,6 +9854,27 @@
         public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger";
 
         /**
+         * Allow GPU debug layers?
+         * 0 = no
+         * 1 = yes
+         * @hide
+         */
+        public static final String ENABLE_GPU_DEBUG_LAYERS = "enable_gpu_debug_layers";
+
+        /**
+         * App allowed to load GPU debug layers
+         * @hide
+         */
+        public static final String GPU_DEBUG_APP = "gpu_debug_app";
+
+        /**
+         * Ordered GPU debug layer list
+         * i.e. <layer1>:<layer2>:...:<layerN>
+         * @hide
+         */
+        public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
+
+        /**
          * Control whether the process CPU usage meter should be shown.
          *
          * @deprecated This functionality is no longer available as of
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2a27220..29baea1 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.text.TextUtils;
 
 import java.util.Map;
@@ -39,13 +40,24 @@
      * @return true if the flag is enabled (either by default in system, or override by user)
      */
     public static boolean isEnabled(Context context, String feature) {
-        // Tries to get feature flag from system property.
-        // Step 1: check if feature flag has any override. Flag name: sys.fflag.override.<feature>
-        String value = SystemProperties.get(FFLAG_OVERRIDE_PREFIX + feature);
+        // Override precedence:
+        // Settings.Global -> sys.fflag.override.* -> sys.fflag.*
+
+        // Step 1: check if feature flag is set in Settings.Global.
+        String value;
+        if (context != null) {
+            value = Settings.Global.getString(context.getContentResolver(), feature);
+            if (!TextUtils.isEmpty(value)) {
+                return Boolean.parseBoolean(value);
+            }
+        }
+
+        // Step 2: check if feature flag has any override. Flag name: sys.fflag.override.<feature>
+        value = SystemProperties.get(FFLAG_OVERRIDE_PREFIX + feature);
         if (!TextUtils.isEmpty(value)) {
             return Boolean.parseBoolean(value);
         }
-        // Step 2: check if feature flag has any default value. Flag name: sys.fflag.<feature>
+        // Step 3: check if feature flag has any default value. Flag name: sys.fflag.<feature>
         value = SystemProperties.get(FFLAG_PREFIX + feature);
         return Boolean.parseBoolean(value);
     }
@@ -53,7 +65,7 @@
     /**
      * Override feature flag to new state.
      */
-    public static void setEnabled(String feature, boolean enabled) {
+    public static void setEnabled(Context context, String feature, boolean enabled) {
         SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false");
     }
 
diff --git a/core/java/android/util/MutableInt.java b/core/java/android/util/MutableInt.java
new file mode 100644
index 0000000..a3d8606
--- /dev/null
+++ b/core/java/android/util/MutableInt.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 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.util;
+
+/**
+ */
+public final class MutableInt {
+    public int value;
+
+    public MutableInt(int value) {
+        this.value = value;
+    }
+}
diff --git a/core/java/android/util/MutableLong.java b/core/java/android/util/MutableLong.java
new file mode 100644
index 0000000..575068e
--- /dev/null
+++ b/core/java/android/util/MutableLong.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 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.util;
+
+/**
+ */
+public final class MutableLong {
+    public long value;
+
+    public MutableLong(long value) {
+        this.value = value;
+    }
+}
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index 55b33a6..2bcd863 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -58,11 +58,12 @@
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    throw new RuntimeException("StatsD service connection lost");
+                    Slog.d(TAG, "Failed to find statsd when adding configuration");
+                    return false;
                 }
                 return service.addConfiguration(configKey, config, pkg, cls);
             } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connect to statsd when getting data");
+                Slog.d(TAG, "Failed to connect to statsd when adding configuration");
                 return false;
             }
         }
@@ -80,11 +81,12 @@
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    throw new RuntimeException("StatsD service connection lost");
+                    Slog.d(TAG, "Failed to find statsd when removing configuration");
+                    return false;
                 }
                 return service.removeConfiguration(configKey);
             } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connect to statsd when getting data");
+                Slog.d(TAG, "Failed to connect to statsd when removing configuration");
                 return false;
             }
         }
@@ -102,7 +104,8 @@
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    throw new RuntimeException("StatsD service connection lost");
+                    Slog.d(TAG, "Failed to find statsd when getting data");
+                    return null;
                 }
                 return service.getData(configKey);
             } catch (RemoteException e) {
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index 449baca..85b7ec8 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -17,6 +17,7 @@
 package android.util.proto;
 
 import android.util.AggStats;
+import android.util.Duration;
 
 /**
  * This class contains a list of helper functions to write common proto in
@@ -36,4 +37,15 @@
         proto.write(AggStats.MAX, max);
         proto.end(aggStatsToken);
     }
+
+    /**
+     * Dump Duration to ProtoOutputStream
+     * @hide
+     */
+    public static void toDuration(ProtoOutputStream proto, long fieldId, long startMs, long endMs) {
+        final long token = proto.start(fieldId);
+        proto.write(Duration.START_MS, startMs);
+        proto.write(Duration.END_MS, endMs);
+        proto.end(token);
+    }
 }
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index 232ff25..defa58e 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -446,49 +446,52 @@
      */
     public static String toString(int gravity) {
         final StringBuilder result = new StringBuilder();
-        if ((gravity & FILL) != 0) {
+        if ((gravity & FILL) == FILL) {
             result.append("FILL").append(' ');
         } else {
-            if ((gravity & FILL_VERTICAL) != 0) {
+            if ((gravity & FILL_VERTICAL) == FILL_VERTICAL) {
                 result.append("FILL_VERTICAL").append(' ');
             } else {
-                if ((gravity & TOP) != 0) {
+                if ((gravity & TOP) == TOP) {
                     result.append("TOP").append(' ');
                 }
-                if ((gravity & BOTTOM) != 0) {
+                if ((gravity & BOTTOM) == BOTTOM) {
                     result.append("BOTTOM").append(' ');
                 }
             }
-            if ((gravity & FILL_HORIZONTAL) != 0) {
+            if ((gravity & FILL_HORIZONTAL) == FILL_HORIZONTAL) {
                 result.append("FILL_HORIZONTAL").append(' ');
             } else {
-                if ((gravity & START) != 0) {
+                if ((gravity & START) == START) {
                     result.append("START").append(' ');
-                } else if ((gravity & LEFT) != 0) {
+                } else if ((gravity & LEFT) == LEFT) {
                     result.append("LEFT").append(' ');
                 }
-                if ((gravity & END) != 0) {
+                if ((gravity & END) == END) {
                     result.append("END").append(' ');
-                } else if ((gravity & RIGHT) != 0) {
+                } else if ((gravity & RIGHT) == RIGHT) {
                     result.append("RIGHT").append(' ');
                 }
             }
         }
-        if ((gravity & CENTER) != 0) {
+        if ((gravity & CENTER) == CENTER) {
             result.append("CENTER").append(' ');
         } else {
-            if ((gravity & CENTER_VERTICAL) != 0) {
+            if ((gravity & CENTER_VERTICAL) == CENTER_VERTICAL) {
                 result.append("CENTER_VERTICAL").append(' ');
             }
-            if ((gravity & CENTER_HORIZONTAL) != 0) {
+            if ((gravity & CENTER_HORIZONTAL) == CENTER_HORIZONTAL) {
                 result.append("CENTER_HORIZONTAL").append(' ');
             }
         }
-        if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
+        if (result.length() == 0) {
+            result.append("NO GRAVITY").append(' ');
+        }
+        if ((gravity & DISPLAY_CLIP_VERTICAL) == DISPLAY_CLIP_VERTICAL) {
             result.append("DISPLAY_CLIP_VERTICAL").append(' ');
         }
-        if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
-            result.append("DISPLAY_CLIP_VERTICAL").append(' ');
+        if ((gravity & DISPLAY_CLIP_HORIZONTAL) == DISPLAY_CLIP_HORIZONTAL) {
+            result.append("DISPLAY_CLIP_HORIZONTAL").append(' ');
         }
         result.deleteCharAt(result.length() - 1);
         return result.toString();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d477ffd..d0c2d86 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -195,6 +195,27 @@
     private final boolean mHapticTextHandleEnabled;
 
     private final Magnifier mMagnifier;
+    private final Runnable mUpdateMagnifierRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mMagnifier.update();
+        }
+    };
+    // Update the magnifier contents whenever anything in the view hierarchy is updated.
+    // Note: this only captures UI thread-visible changes, so it's a known issue that an animating
+    // VectorDrawable or Ripple animation will not trigger capture, since they're owned by
+    // RenderThread.
+    private final ViewTreeObserver.OnDrawListener mMagnifierOnDrawListener =
+            new ViewTreeObserver.OnDrawListener() {
+        @Override
+        public void onDraw() {
+            if (mMagnifier != null) {
+                // Posting the method will ensure that updating the magnifier contents will
+                // happen right after the rendering of the current frame.
+                mTextView.post(mUpdateMagnifierRunnable);
+            }
+        }
+    };
 
     // Used to highlight a word when it is corrected by the IME
     private CorrectionHighlighter mCorrectionHighlighter;
@@ -415,15 +436,21 @@
         }
 
         final ViewTreeObserver observer = mTextView.getViewTreeObserver();
-        // No need to create the controller.
-        // The get method will add the listener on controller creation.
-        if (mInsertionPointCursorController != null) {
-            observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+        if (observer.isAlive()) {
+            // No need to create the controller.
+            // The get method will add the listener on controller creation.
+            if (mInsertionPointCursorController != null) {
+                observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+            }
+            if (mSelectionModifierCursorController != null) {
+                mSelectionModifierCursorController.resetTouchOffsets();
+                observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+            }
+            if (FLAG_USE_MAGNIFIER) {
+                observer.addOnDrawListener(mMagnifierOnDrawListener);
+            }
         }
-        if (mSelectionModifierCursorController != null) {
-            mSelectionModifierCursorController.resetTouchOffsets();
-            observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
-        }
+
         updateSpellCheckSpans(0, mTextView.getText().length(),
                 true /* create the spell checker if needed */);
 
@@ -472,6 +499,13 @@
             mSpellChecker = null;
         }
 
+        if (FLAG_USE_MAGNIFIER) {
+            final ViewTreeObserver observer = mTextView.getViewTreeObserver();
+            if (observer.isAlive()) {
+                observer.removeOnDrawListener(mMagnifierOnDrawListener);
+            }
+        }
+
         hideCursorAndSpanControllers();
         stopTextActionModeWithPreservingSelection();
     }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 91bc681..22bfcc3 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -30,6 +30,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -134,6 +135,13 @@
     }
 
     /**
+     * Checks if given map is null or has zero elements.
+     */
+    public static boolean isEmpty(@Nullable Map<?, ?> map) {
+        return map == null || map.isEmpty();
+    }
+
+    /**
      * Checks if given array is null or has zero elements.
      */
     public static <T> boolean isEmpty(@Nullable T[] array) {
diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java
index f6741c3..f79a7f5 100644
--- a/core/java/com/android/internal/widget/Magnifier.java
+++ b/core/java/com/android/internal/widget/Magnifier.java
@@ -18,34 +18,33 @@
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UiThread;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.PixelCopy;
+import android.view.Surface;
+import android.view.SurfaceView;
 import android.view.View;
-import android.view.ViewRootImpl;
 import android.widget.ImageView;
 import android.widget.PopupWindow;
 
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 
-import java.util.Timer;
-import java.util.TimerTask;
-
 /**
  * Android magnifier widget. Can be used by any view which is attached to window.
  */
 public final class Magnifier {
-    private static final String LOG_TAG = "magnifier";
-    private static final int MAGNIFIER_REFRESH_RATE_MS = 33; // ~30fps
-    // The view for which this magnifier is attached.
+    // Use this to specify that a previous configuration value does not exist.
+    private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+    // The view to which this magnifier is attached.
     private final View mView;
     // The window containing the magnifier.
     private final PopupWindow mWindow;
@@ -64,8 +63,12 @@
     private final Handler mPixelCopyHandler = Handler.getMain();
     // Current magnification scale.
     private final float mZoomScale;
-    // Timer used to schedule the copy task.
-    private Timer mTimer;
+    // Variables holding previous states, used for detecting redundant calls and invalidation.
+    private final Point mPrevStartCoordsInSurface = new Point(
+            NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+    private final PointF mPrevPosInView = new PointF(
+            NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+    private final Rect mPixelCopyRequestRect = new Rect();
 
     /**
      * Initializes a magnifier.
@@ -91,8 +94,8 @@
         mWindow.setTouchable(false);
         mWindow.setBackgroundDrawable(null);
 
-        final int bitmapWidth = (int) (mWindowWidth / mZoomScale);
-        final int bitmapHeight = (int) (mWindowHeight / mZoomScale);
+        final int bitmapWidth = Math.round(mWindowWidth / mZoomScale);
+        final int bitmapHeight = Math.round(mWindowHeight / mZoomScale);
         mBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
         getImageView().setImageBitmap(mBitmap);
     }
@@ -106,32 +109,29 @@
      *        relative to the view. The lower end is clamped to 0
      */
     public void show(@FloatRange(from=0) float xPosInView, @FloatRange(from=0) float yPosInView) {
-        if (xPosInView < 0) {
-            xPosInView = 0;
-        }
-
-        if (yPosInView < 0) {
-            yPosInView = 0;
-        }
+        xPosInView = Math.max(0, xPosInView);
+        yPosInView = Math.max(0, yPosInView);
 
         configureCoordinates(xPosInView, yPosInView);
 
-        if (mTimer == null) {
-            mTimer = new Timer();
-            mTimer.schedule(new TimerTask() {
-                @Override
-                public void run() {
-                    performPixelCopy();
-                }
-            }, 0 /* delay */, MAGNIFIER_REFRESH_RATE_MS);
-        }
+        // Clamp startX value to avoid distorting the rendering of the magnifier content.
+        final int startX = Math.max(0, Math.min(
+                mCenterZoomCoords.x - mBitmap.getWidth() / 2,
+                mView.getWidth() - mBitmap.getWidth()));
+        final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
 
-        if (mWindow.isShowing()) {
-            mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
-                    mWindow.getHeight());
-        } else {
-            mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
-                    mWindowCoords.x, mWindowCoords.y);
+        if (startX != mPrevStartCoordsInSurface.x || startY != mPrevStartCoordsInSurface.y) {
+            performPixelCopy(startX, startY);
+
+            mPrevPosInView.x = xPosInView;
+            mPrevPosInView.y = yPosInView;
+
+            if (mWindow.isShowing()) {
+                mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
+                        mWindow.getHeight());
+            } else {
+                mWindow.showAtLocation(mView, Gravity.NO_GRAVITY, mWindowCoords.x, mWindowCoords.y);
+            }
         }
     }
 
@@ -140,11 +140,18 @@
      */
     public void dismiss() {
         mWindow.dismiss();
+    }
 
-        if (mTimer != null) {
-            mTimer.cancel();
-            mTimer.purge();
-            mTimer = null;
+    /**
+     * Forces the magnifier to update its content. It uses the previous coordinates passed to
+     * {@link #show(float, float)}. This only happens if the magnifier is currently showing.
+     *
+     * @hide
+     */
+    public void update() {
+        if (mWindow.isShowing()) {
+            // Update the contents shown in the magnifier.
+            performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y);
         }
     }
 
@@ -170,13 +177,22 @@
     }
 
     private void configureCoordinates(float xPosInView, float yPosInView) {
-        final int[] coordinatesOnScreen = new int[2];
-        mView.getLocationOnScreen(coordinatesOnScreen);
-        final float posXOnScreen = xPosInView + coordinatesOnScreen[0];
-        final float posYOnScreen = yPosInView + coordinatesOnScreen[1];
+        final float posX;
+        final float posY;
 
-        mCenterZoomCoords.x = (int) posXOnScreen;
-        mCenterZoomCoords.y = (int) posYOnScreen;
+        if (mView instanceof SurfaceView) {
+            // No offset required if the backing Surface matches the size of the SurfaceView.
+            posX = xPosInView;
+            posY = yPosInView;
+        } else {
+            final int[] coordinatesInSurface = new int[2];
+            mView.getLocationInSurface(coordinatesInSurface);
+            posX = xPosInView + coordinatesInSurface[0];
+            posY = yPosInView + coordinatesInSurface[1];
+        }
+
+        mCenterZoomCoords.x = Math.round(posX);
+        mCenterZoomCoords.y = Math.round(posY);
 
         final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize(
                 R.dimen.magnifier_offset);
@@ -184,34 +200,36 @@
         mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
     }
 
-    private void performPixelCopy() {
-        final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
-        int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
+    private void performPixelCopy(final int startXInSurface, final int startYInSurface) {
+        final Surface surface = getValidViewSurface();
+        if (surface != null) {
+            mPixelCopyRequestRect.set(startXInSurface, startYInSurface,
+                    startXInSurface + mBitmap.getWidth(), startYInSurface + mBitmap.getHeight());
 
-        // Clamp startX value to avoid distorting the rendering of the magnifier content.
-        if (rawStartX < 0) {
-            rawStartX = 0;
-        } else if (rawStartX + mBitmap.getWidth() > mView.getWidth()) {
-            rawStartX = mView.getWidth() - mBitmap.getWidth();
-        }
-
-        final int startX = rawStartX;
-        final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
-
-        if (viewRootImpl != null && viewRootImpl.mSurface != null
-                && viewRootImpl.mSurface.isValid()) {
-            PixelCopy.request(
-                    viewRootImpl.mSurface,
-                    new Rect(startX, startY, startX + mBitmap.getWidth(),
-                            startY + mBitmap.getHeight()),
-                    mBitmap,
-                    result -> getImageView().invalidate(),
+            PixelCopy.request(surface, mPixelCopyRequestRect, mBitmap,
+                    result -> {
+                        getImageView().invalidate();
+                        mPrevStartCoordsInSurface.x = startXInSurface;
+                        mPrevStartCoordsInSurface.y = startYInSurface;
+                    },
                     mPixelCopyHandler);
-        } else {
-            Log.d(LOG_TAG, "Could not perform PixelCopy request");
         }
     }
 
+    @Nullable
+    private Surface getValidViewSurface() {
+        final Surface surface;
+        if (mView instanceof SurfaceView) {
+            surface = ((SurfaceView) mView).getHolder().getSurface();
+        } else if (mView.getViewRootImpl() != null) {
+            surface = mView.getViewRootImpl().mSurface;
+        } else {
+            surface = null;
+        }
+
+        return (surface != null && surface.isValid()) ? surface : null;
+    }
+
     private ImageView getImageView() {
         return mWindow.getContentView().findViewById(R.id.magnifier_image);
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3552e43..9d036dc 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,7 +42,6 @@
         "com_google_android_gles_jni_EGLImpl.cpp",
         "com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
         "android_app_Activity.cpp",
-        "android_app_ApplicationLoaders.cpp",
         "android_app_NativeActivity.cpp",
         "android_app_admin_SecurityLog.cpp",
         "android_opengl_EGL14.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8977891..2a65cde 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -187,7 +187,6 @@
 extern int register_android_app_backup_FullBackup(JNIEnv *env);
 extern int register_android_app_Activity(JNIEnv *env);
 extern int register_android_app_ActivityThread(JNIEnv *env);
-extern int register_android_app_ApplicationLoaders(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
 extern int register_android_media_RemoteDisplay(JNIEnv *env);
 extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
@@ -1456,7 +1455,6 @@
     REG_JNI(register_android_app_backup_FullBackup),
     REG_JNI(register_android_app_Activity),
     REG_JNI(register_android_app_ActivityThread),
-    REG_JNI(register_android_app_ApplicationLoaders),
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_util_jar_StrictJarFile),
     REG_JNI(register_android_view_InputChannel),
diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp
deleted file mode 100644
index 8bbf24a..0000000
--- a/core/jni/android_app_ApplicationLoaders.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ApplicationLoaders"
-
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativeloader/native_loader.h>
-#include <vulkan/vulkan_loader_data.h>
-
-#include "core_jni_helpers.h"
-
-static void setupVulkanLayerPath_native(JNIEnv* env, jobject clazz,
-        jobject classLoader, jstring librarySearchPath) {
-    android_namespace_t* ns = android::FindNamespaceByClassLoader(env, classLoader);
-    ScopedUtfChars layerPathChars(env, librarySearchPath);
-
-    vulkan::LoaderData& loader_data = vulkan::LoaderData::GetInstance();
-    if (loader_data.layer_path.empty()) {
-        loader_data.layer_path = layerPathChars.c_str();
-        loader_data.app_namespace = ns;
-    } else {
-        ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'",
-                layerPathChars.c_str(), ns);
-    }
-}
-
-static const JNINativeMethod g_methods[] = {
-    { "setupVulkanLayerPath", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
-      reinterpret_cast<void*>(setupVulkanLayerPath_native) },
-};
-
-static const char* const kApplicationLoadersName = "android/app/ApplicationLoaders";
-
-namespace android
-{
-
-int register_android_app_ApplicationLoaders(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, kApplicationLoadersName, g_methods, NELEM(g_methods));
-}
-
-} // namespace android
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index f749488..4ecfd4b 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -18,6 +18,7 @@
 
 #include <graphicsenv/GraphicsEnv.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <nativeloader/native_loader.h>
 #include "core_jni_helpers.h"
 
 namespace {
@@ -27,8 +28,23 @@
     android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
 }
 
+void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
+    android_namespace_t* appNamespace = android::FindNamespaceByClassLoader(env, classLoader);
+    ScopedUtfChars layerPathsChars(env, layerPaths);
+    android::GraphicsEnv::getInstance().setLayerPaths(appNamespace, layerPathsChars.c_str());
+}
+
+void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) {
+    if (layers != nullptr) {
+        ScopedUtfChars layersChars(env, layers);
+        android::GraphicsEnv::getInstance().setDebugLayers(layersChars.c_str());
+    }
+}
+
 const JNINativeMethod g_methods[] = {
     { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+    { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
+    { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
 };
 
 const char* const kGraphicsEnvironmentName = "android/os/GraphicsEnvironment";
diff --git a/core/proto/android/app/notification.proto b/core/proto/android/app/notification.proto
new file mode 100644
index 0000000..5376b0e
--- /dev/null
+++ b/core/proto/android/app/notification.proto
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.Notification object.
+ * Deprecated fields are not included in the proto.
+ */
+message NotificationProto {
+    optional string channel_id = 1;
+    optional bool has_ticker_text = 2;
+    optional int32 flags = 3;
+    optional int32 color = 4;
+    optional string category = 5;
+    optional string group_key = 6;
+    optional string sort_key = 7;
+    optional int32 action_length = 8;
+
+    // If this field is not set, then the value is unknown.
+    enum Visibility {
+        VISIBILITY_SECRET = -1;
+        VISIBILITY_PRIVATE = 0;
+        VISIBILITY_PUBLIC = 1;
+    }
+    optional Visibility visibility = 9;
+    optional NotificationProto public_version = 10;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index f68f3a4..6c5206c 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -143,7 +143,11 @@
         (section).args = "activity --proto broadcasts"
     ];
 
-    optional com.android.server.am.proto.ServiceProto amservices = 3014;
+    optional com.android.server.am.proto.ActiveServicesProto amservices = 3014 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "activity --proto service"
+    ];
+
     optional com.android.server.am.proto.ProcessProto amprocesses = 3015;
 
     optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 764288c..5d5aea2 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -305,6 +305,9 @@
     optional SettingProto preferred_network_mode = 226;
     optional SettingProto debug_app = 227;
     optional SettingProto wait_for_debugger = 228;
+    optional SettingProto enable_gpu_debug_layers = 342;
+    optional SettingProto gpu_debug_app = 343;
+    optional SettingProto gpu_debug_layers = 344;
     optional SettingProto low_power_mode = 229;
     optional SettingProto low_power_mode_trigger_level = 230;
     optional SettingProto always_finish_activities = 231;
@@ -384,7 +387,7 @@
     optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
     optional SettingProto notification_snooze_options = 341;
 
-    // Next tag = 342;
+    // Next tag = 345;
 }
 
 message SecureSettingsProto {
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index c57cb72..889842c 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -15,11 +15,14 @@
  */
 
 syntax = "proto2";
+
+import "frameworks/base/core/proto/android/app/notification.proto";
 import "frameworks/base/core/proto/android/content/intent.proto";
 import "frameworks/base/core/proto/android/server/intentresolver.proto";
 import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 import "frameworks/base/core/proto/android/graphics/rect.proto";
 import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/util/common.proto";
 
 package com.android.server.am.proto;
 
@@ -30,11 +33,12 @@
 
   optional BroadcastProto broadcasts = 2;
 
-  optional ServiceProto services = 3;
+  optional ActiveServicesProto services = 3;
 
   optional ProcessProto processes = 4;
 }
 
+// "dumpsys activity --proto activities"
 message ActivityStackSupervisorProto {
   optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
   repeated ActivityDisplayProto displays = 2;
@@ -90,6 +94,7 @@
   optional bool keyguard_occluded = 2;
 }
 
+// "dumpsys activity --proto broadcasts"
 message BroadcastProto {
   repeated ReceiverListProto  receiver_list = 1;
 
@@ -164,10 +169,158 @@
   repeated StickyAction actions = 2;
 }
 
-message ServiceProto {
-  // TODO: "dumpsys activity --proto services"
+// "dumpsys activity --proto service"
+message ActiveServicesProto {
+
+  message ServicesByUser {
+    optional int32 user_id = 1;
+    repeated ServiceRecordProto service_records = 2;
+  }
+  repeated ServicesByUser services_by_users = 1;
 }
 
+// corresponds to ActivityManagerService.GrantUri Java class
+message GrantUriProto {
+  optional int32 source_user_id = 1;
+  optional string uri = 2;
+}
+
+message NeededUriGrantsProto {
+  optional string target_package = 1;
+  optional int32 target_uid = 2;
+  optional int32 flags = 3;
+
+  repeated GrantUriProto grants = 4;
+}
+
+message UriPermissionOwnerProto {
+  optional string owner = 1;
+  repeated GrantUriProto read_perms = 2;
+  repeated GrantUriProto write_perms = 3;
+}
+
+message ServiceRecordProto {
+  optional string short_name = 1;
+  optional string hex_hash = 2;
+  optional bool is_running = 3; // false if the application service is null
+  optional int32 pid = 4;
+  optional .android.content.IntentProto intent = 5;
+  optional string package_name = 6;
+  optional string process_name = 7;
+  optional string permission = 8;
+
+  message AppInfo {
+    optional string base_dir = 1;
+    optional string res_dir = 2;
+    optional string data_dir = 3;
+  }
+  optional AppInfo appinfo = 9;
+  optional ProcessRecordProto app = 10;
+  optional ProcessRecordProto isolated_proc = 11;
+  optional bool whitelist_manager = 12;
+  optional bool delayed = 13;
+
+  message Foreground {
+    optional int32 id = 1;
+    optional .android.app.NotificationProto notification = 2;
+  }
+  optional Foreground foreground = 14;
+
+  optional .android.util.Duration create_real_time = 15;
+  optional .android.util.Duration starting_bg_timeout = 16;
+  optional .android.util.Duration last_activity_time = 17;
+  optional .android.util.Duration restart_time = 18;
+  optional bool created_from_fg = 19;
+
+  // variables used to track states related to service start
+  message Start {
+    optional bool start_requested = 1;
+    optional bool delayed_stop = 2;
+    optional bool stop_if_killed = 3;
+    optional bool call_start = 4;
+    optional int32 last_start_id = 5;
+  }
+  optional Start start = 20;
+
+  message ExecuteNesting {
+    optional int32 execute_nesting = 1;
+    optional bool execute_fg = 2;
+    optional .android.util.Duration executing_start = 3;
+  }
+  optional ExecuteNesting execute = 21;
+
+  optional .android.util.Duration destory_time = 22;
+
+  message Crash {
+    optional int32 restart_count = 1;
+    optional .android.util.Duration restart_delay = 2;
+    optional .android.util.Duration next_restart_time = 3;
+    optional int32 crash_count = 4;
+  }
+  optional Crash crash = 23;
+
+  message StartItemProto {
+    optional int32 id = 1;
+    optional .android.util.Duration duration = 2;
+    optional int32 delivery_count = 3;
+    optional int32 done_executing_count = 4;
+    optional .android.content.IntentProto intent = 5;
+    optional NeededUriGrantsProto needed_grants = 6;
+    optional UriPermissionOwnerProto uri_permissions = 7;
+  }
+  repeated StartItemProto delivered_starts = 24;
+  repeated StartItemProto pending_starts = 25;
+
+  repeated IntentBindRecordProto bindings = 26;
+  repeated ConnectionRecordProto connections = 27;
+}
+
+message ConnectionRecordProto {
+  optional string hex_hash = 1;
+  optional int32 user_id = 2;
+
+  enum Flag {
+    AUTO_CREATE = 0;
+    DEBUG_UNBIND = 1;
+    NOT_FG = 2;
+    IMPORTANT_BG = 3;
+    ABOVE_CLIENT = 4;
+    ALLOW_OOM_MANAGEMENT = 5;
+    WAIVE_PRIORITY = 6;
+    IMPORTANT = 7;
+    ADJUST_WITH_ACTIVITY = 8;
+    FG_SERVICE_WHILE_WAKE = 9;
+    FG_SERVICE = 10;
+    TREAT_LIKE_ACTIVITY = 11;
+    VISIBLE = 12;
+    SHOWING_UI = 13;
+    NOT_VISIBLE = 14;
+    DEAD = 15;
+  }
+  repeated Flag flags = 3;
+  optional string service_name = 4;
+  optional string conn_hex_hash = 5;
+}
+
+message AppBindRecordProto {
+  optional string hex_hash = 1;
+  optional ProcessRecordProto client = 2;
+  repeated ConnectionRecordProto connections = 3;
+}
+
+message IntentBindRecordProto {
+  optional string hex_hash = 1;
+  optional bool is_create = 2;
+  optional .android.content.IntentProto intent = 3;
+  optional string binder = 4;
+  optional bool requested = 5;
+  optional bool received = 6;
+  optional bool has_bound = 7;
+  optional bool do_rebind = 8;
+
+  repeated AppBindRecordProto apps = 9;
+}
+
+// TODO: "dumpsys activity --proto processes"
 message ProcessProto {
-  // TODO: "dumpsys activity --proto processes"
 }
diff --git a/core/proto/android/util/common.proto b/core/proto/android/util/common.proto
index 429c3cad..308ef70 100644
--- a/core/proto/android/util/common.proto
+++ b/core/proto/android/util/common.proto
@@ -30,3 +30,13 @@
 
     optional int64 max = 3;
 }
+
+/**
+ * Very basic data structure to represent Duration.
+ */
+message Duration {
+
+    optional int64 start_ms = 1;
+
+    optional int64 end_ms = 2;
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index d36ed63..0d35f4e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -356,6 +356,9 @@
                     Settings.Global.USE_GOOGLE_MAIL,
                     Settings.Global.VT_IMS_ENABLED,
                     Settings.Global.WAIT_FOR_DEBUGGER,
+                    Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
+                    Settings.Global.GPU_DEBUG_APP,
+                    Settings.Global.GPU_DEBUG_LAYERS,
                     Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
index c5d175b..b8c4123 100644
--- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -22,6 +22,7 @@
 
 import android.content.Context;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -51,6 +52,7 @@
     }
 
     private void cleanup() {
+        Settings.Global.putString(mContext.getContentResolver(), TEST_FEATURE_NAME, "");
         SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "");
         SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "");
     }
@@ -63,7 +65,7 @@
     }
 
     @Test
-    public void testGetFlag_override_shouldReturnTrue() {
+    public void testGetFlag_adb_override_shouldReturnTrue() {
         SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "false");
         SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "true");
 
@@ -71,10 +73,20 @@
     }
 
     @Test
+    public void testGetFlag_settings_override_shouldReturnTrue() {
+        SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "false");
+        SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "false");
+
+        Settings.Global.putString(mContext.getContentResolver(), TEST_FEATURE_NAME, "true");
+
+        assertTrue(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
+    }
+
+    @Test
     public void testSetEnabled_shouldSetOverrideFlag() {
         assertFalse(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
 
-        FeatureFlagUtils.setEnabled(TEST_FEATURE_NAME, true);
+        FeatureFlagUtils.setEnabled(null /* context */, TEST_FEATURE_NAME, true);
 
         assertEquals(SystemProperties.get(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, null),
                 "");
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 7b2fa76..81164d5 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -112,7 +112,7 @@
 # Run sanity tests on fonts on checkbuild
 checkbuild: fontchain_lint
 
-FONTCHAIN_LINTER := frameworks/base/tools/fonts/fontchain_lint.py
+FONTCHAIN_LINTER := $(HOST_OUT_EXECUTABLES)/fontchain_linter
 ifeq ($(MINIMAL_FONT_FOOTPRINT),true)
 CHECK_EMOJI := false
 else
@@ -121,5 +121,4 @@
 
 .PHONY: fontchain_lint
 fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img
-	PYTHONPATH=$$PYTHONPATH:external/fonttools/Lib \
-	python $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
+	$(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 24ce1e4..5c577ae 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -2,9 +2,10 @@
     name: "hwui_defaults",
     defaults: [
         "hwui_static_deps",
-        "skia_deps"
+        "skia_deps",
         //"hwui_bugreport_font_cache_usage",
         //"hwui_compile_for_perf",
+        "hwui_pgo",
     ],
 
     cpp_std: "c++17",
@@ -109,6 +110,22 @@
     include_dirs: ["frameworks/native/opengl/libs/GLES2"],
 }
 
+// Build libhwui with PGO by default.
+// Location of PGO profile data is defined in build/soong/cc/pgo.go
+// and is separate from hwui.
+// To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
+// or set enable_profile_use property to false.
+cc_defaults {
+    name: "hwui_pgo",
+
+    pgo: {
+        instrumentation: true,
+        profile_file: "hwui/hwui.profdata",
+        benchmarks: ["hwui"],
+        enable_profile_use: false,
+    },
+}
+
 // ------------------------
 // library
 // ------------------------
@@ -255,18 +272,6 @@
         // Has moderate overhead
         "hwui_enable_opengl_validation",
     ],
-
-    // Build libhwui with PGO by default.
-    // Location of PGO profile data is defined in build/soong/cc/pgo.go
-    // and is separate from hwui.
-    // To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
-    // or set enable_profile_use property to false.
-    pgo: {
-        instrumentation: true,
-        profile_file: "hwui/hwui.profdata",
-        benchmarks: ["hwui"],
-        enable_profile_use: false,
-    },
 }
 
 // ------------------------
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index ce41849..faea9b2 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -110,6 +110,12 @@
     void end(long long token);
 
     /**
+     * Returns how many bytes are buffered in ProtoOutputStream.
+     * Notice, this is not the actual(compact) size of the output data.
+     */
+    size_t bytesWritten();
+
+    /**
      * Flushes the protobuf data out to given fd. When the following functions are called,
      * it is not able to write to ProtoOutputStream any more since the data is compact.
      */
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 9d8ee72..1904d40 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -295,6 +295,12 @@
     }
 }
 
+size_t
+ProtoOutputStream::bytesWritten()
+{
+    return mBuffer.size();
+}
+
 bool
 ProtoOutputStream::compact() {
     if (mCompact) return true;
diff --git a/location/Android.mk b/location/Android.mk
index feeb8ce..50509c6 100644
--- a/location/Android.mk
+++ b/location/Android.mk
@@ -14,4 +14,4 @@
 
 LOCAL_PATH := $(call my-dir)
 
-include $(call all-makefiles-under, $(LOCAL_PATH))
+include $(call all-subdir-makefiles, $(LOCAL_PATH))
\ No newline at end of file
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index 902cd96..73b2bb5 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -10,5 +10,13 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksLocationTests
 
-include $(BUILD_PACKAGE)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    core-test-rules \
+    guava \
+    mockito-target-minus-junit4 \
+    frameworks-base-testutils \
+    truth-prebuilt \
 
+LOCAL_COMPATIBILITY_SUITE := device-tests
+include $(BUILD_PACKAGE)
diff --git a/location/tests/locationtests/AndroidManifest.xml b/location/tests/locationtests/AndroidManifest.xml
index 1d9df0f..ddb8ea6 100644
--- a/location/tests/locationtests/AndroidManifest.xml
+++ b/location/tests/locationtests/AndroidManifest.xml
@@ -4,9 +4,9 @@
      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.
@@ -23,13 +23,13 @@
     <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-    
+
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
 
     <instrumentation
-    	android:name="android.test.InstrumentationTestRunner"
-    	android:targetPackage="com.android.frameworks.locationtests"
-    	android:label="Frameworks Location Tests" />
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.frameworks.locationtests"
+        android:label="Frameworks Location Tests" />
 </manifest>
diff --git a/location/tests/locationtests/AndroidTest.xml b/location/tests/locationtests/AndroidTest.xml
new file mode 100644
index 0000000..0c5b7cc
--- /dev/null
+++ b/location/tests/locationtests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Frameworks Location API Tests.">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="FrameworksLocationTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-tag" value="FrameworksLocationTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.locationtests" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/location/tests/locationtests/src/android/location/GnssStatusTest.java b/location/tests/locationtests/src/android/location/GnssStatusTest.java
new file mode 100644
index 0000000..79ea0d6
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssStatusTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link GnssStatus}.
+ */
+@SmallTest
+public class GnssStatusTest extends TestCase {
+
+  private static final String TAG = GnssStatusTest.class.getSimpleName();
+  public void setUp() throws Exception {
+    super.setUp();
+  }
+
+  /*
+   * Create {@link GnssStatus} with default value, verify whether its fields are set correctly.
+   *
+   */
+  public void testEmptyGnssStatus() throws Exception {
+    Log.i(TAG, "testEmptyGnssStatus");
+    List<SatelliteInfo> svInfos = new ArrayList<>();
+    GnssStatus gnssStatus = createGnssStatus(svInfos);
+    verifyGnssStatus(svInfos, gnssStatus);
+  }
+
+  /*
+   * Create {@link GnssStatus} with only one satellite info, verify whether its fields are set
+   * correctly.
+   */
+  public void testOneSatelliteGnssStatus() throws Exception {
+    Log.i(TAG, "testOneSatelliteGnssStatus");
+    List<SatelliteInfo> svInfos = new ArrayList<>();
+    SatelliteInfo svInfo =
+        new SatelliteInfo(100,1, true, true, true, true, 100f, 20.3f, 45.5f, 100.23f);
+    svInfos.add(svInfo);
+    GnssStatus gnssStatus = createGnssStatus(svInfos);
+    verifyGnssStatus(svInfos, gnssStatus);
+  }
+
+  /*
+   * Create {@link GnssStatus} with multiple satellite info, verify whether its fields are set
+   * correctly.
+   */
+  public void testMultipleSatellitesGnssStatus() throws Exception {
+    Log.i(TAG, "testMultipleSatellitesGnssStatus");
+    List<SatelliteInfo> svInfos = new ArrayList<>();
+    SatelliteInfo svInfo1 =
+        new SatelliteInfo(20, 1,true, true, true, true, 10.1f, 20.3f, 45.5f, 111.23f);
+    SatelliteInfo svInfo2 =
+        new SatelliteInfo(50, 2, true, false, true, false, 20.2f, 21.3f, 46.5f, 222.23f);
+    SatelliteInfo svInfo3 =
+        new SatelliteInfo(192, 3, false, true, false, true, 30.3f, 22.3f, 47.5f, 333.23f);
+    SatelliteInfo svInfo4 =
+        new SatelliteInfo(250, 4, false, false, false, false, 40.4f, 23.3f, 48.5f, 444.23f);
+    svInfos.add(svInfo1);
+    svInfos.add(svInfo2);
+    svInfos.add(svInfo3);
+    svInfos.add(svInfo4);
+    GnssStatus gnssStatus = createGnssStatus(svInfos);
+    verifyGnssStatus(svInfos, gnssStatus);
+  }
+
+  private void verifyGnssStatus(List<SatelliteInfo> svInfos, GnssStatus gnssStatus) {
+    Log.i(TAG, String.format("Verifing {0} satellites info.",svInfos.size()));
+    assertEquals(TAG + "::SatelliteCount", svInfos.size(),
+        gnssStatus.getSatelliteCount());
+    for (int i = 0; i< svInfos.size(); i++) {
+      SatelliteInfo svInfo = svInfos.get(i);
+      assertEquals(TAG + "::Svid", svInfo.mSvid, gnssStatus.getSvid(i));
+      assertEquals(TAG + "::ConstellationType", svInfo.mConstellationType,
+          gnssStatus.getConstellationType(i));
+      assertEquals(TAG + "::Cn0DbHz", svInfo.mCn0DbHz, gnssStatus.getCn0DbHz(i));
+      assertEquals(TAG + "::Elevation", svInfo.mElevation,
+          gnssStatus.getElevationDegrees(i));
+      assertEquals(TAG + "::Azimuth", svInfo.mAzimuth, gnssStatus.getAzimuthDegrees(i));
+      assertEquals(TAG + "::CarrierFrequencyHz", svInfo.mCarrierFrequency,
+          gnssStatus.getCarrierFrequencyHz(i));
+      assertEquals(TAG + "::hasEphemerisData", svInfo.mHasEphemris,
+          gnssStatus.hasEphemerisData(i));
+      assertEquals(TAG + "::HasAlmanacData", svInfo.mHasAlmanac,
+          gnssStatus.hasAlmanacData(i));
+      assertEquals(TAG + "::UsedInFix", svInfo.mUsedInFix, gnssStatus.usedInFix(i));
+      assertEquals(TAG + "::HasCarrierFrequencyHz", svInfo.mHasCarriesFrequency,
+          gnssStatus.hasCarrierFrequencyHz(i));
+    }
+  }
+
+  private static GnssStatus createGnssStatus(List<SatelliteInfo> svInfos) throws Exception {
+    Class<?> intClass = Integer.TYPE;
+    Class<?> floatArrayClass = Class.forName("[F");
+    Class<?> intArrayClass = Class.forName("[I");
+    Class[] cArg = new Class[6];
+    cArg[0] = intClass;
+    cArg[1] = intArrayClass;
+    cArg[2] = floatArrayClass;
+    cArg[3] = floatArrayClass;
+    cArg[4] = floatArrayClass;
+    cArg[5] = floatArrayClass;
+    Constructor<GnssStatus>  ctor = GnssStatus.class.getDeclaredConstructor(cArg);
+    ctor.setAccessible(true);
+    return ctor.newInstance(svInfos.size(),
+        SatelliteInfo.getSvidWithFlagsArray(svInfos),
+        SatelliteInfo.getCn0sArray(svInfos),
+        SatelliteInfo.getElevationsArray(svInfos),
+        SatelliteInfo.getAzimuthsArray(svInfos),
+        SatelliteInfo.getCarrierFrequencyArray(svInfos));
+  }
+}
diff --git a/location/tests/locationtests/src/android/location/GpsStatusTest.java b/location/tests/locationtests/src/android/location/GpsStatusTest.java
deleted file mode 100644
index 316e88d..0000000
--- a/location/tests/locationtests/src/android/location/GpsStatusTest.java
+++ /dev/null
@@ -1,356 +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 android.location;
-
-import junit.framework.TestCase;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * Unit tests for {@link GpsStatus}.
- */
-@SmallTest
-public class GpsStatusTest extends TestCase {
-
-    private static final int MAX_VALUE = 250;
-
-    private final Random mRandom = new Random();
-
-    private GpsStatus mStatus;
-    private int mCount;
-    private int[] mPrns;
-    private float[] mCn0s;
-    private float[] mElevations;
-    private float[] mAzimuth;
-    private int mEphemerisMask;
-    private int mAlmanacMask;
-    private int mUsedInFixMask;
-
-    public void setUp() throws Exception {
-        super.setUp();
-        mStatus = createGpsStatus();
-        generateSatellitesData(generateInt());
-    }
-
-    public void testEmptyGpsStatus() throws Exception {
-        verifyIsEmpty(mStatus);
-    }
-
-    public void testGpsStatusIterator() throws Exception {
-        generateSatellitesData(2);
-        setSatellites(mStatus);
-        Iterator<GpsSatellite> iterator = mStatus.getSatellites().iterator();
-        assertTrue("hasNext(1)", iterator.hasNext());
-        assertTrue("hasNext(1) does not overflow", iterator.hasNext());
-        GpsSatellite satellite1 = iterator.next();
-        assertNotNull("satellite", satellite1);
-        assertTrue("hasNext(2)", iterator.hasNext());
-        assertTrue("hasNext(2) does not overflow", iterator.hasNext());
-        GpsSatellite satellite2 = iterator.next();
-        assertNotNull("satellite", satellite2);
-        assertFalse("hasNext() no elements", iterator.hasNext());
-    }
-
-    public void testTtff() throws Exception {
-        int testTtff = generateInt();
-        set(mStatus, testTtff);
-        verifyTtff(mStatus, testTtff);
-    }
-
-    public void testCopyTtff() throws Exception {
-        int testTtff = generateInt();
-        verifyTtff(mStatus, 0);
-
-        GpsStatus otherStatus = createGpsStatus();
-        set(otherStatus, testTtff);
-        verifyTtff(otherStatus, testTtff);
-
-        set(mStatus, otherStatus);
-        verifyTtff(mStatus, testTtff);
-    }
-
-    public void testSetSatellites() throws Exception {
-        setSatellites(mStatus);
-        verifySatellites(mStatus);
-    }
-
-    public void testCopySatellites() throws Exception {
-        verifyIsEmpty(mStatus);
-
-        GpsStatus otherStatus = createGpsStatus();
-        setSatellites(otherStatus);
-        verifySatellites(otherStatus);
-
-        set(mStatus, otherStatus);
-        verifySatellites(mStatus);
-    }
-
-    public void testOverrideSatellites() throws Exception {
-        setSatellites(mStatus);
-        verifySatellites(mStatus);
-
-        GpsStatus otherStatus = createGpsStatus();
-        generateSatellitesData(mCount, true /* reusePrns */);
-        setSatellites(otherStatus);
-        verifySatellites(otherStatus);
-
-        set(mStatus, otherStatus);
-        verifySatellites(mStatus);
-    }
-
-    public void testAddSatellites() throws Exception {
-        int count = 10;
-        generateSatellitesData(count);
-        setSatellites(mStatus);
-        verifySatellites(mStatus);
-
-        GpsStatus otherStatus = createGpsStatus();
-        generateSatellitesData(count);
-        setSatellites(otherStatus);
-        verifySatellites(otherStatus);
-
-        set(mStatus, otherStatus);
-        verifySatellites(mStatus);
-    }
-
-    public void testAddMoreSatellites() throws Exception {
-        int count = 25;
-        generateSatellitesData(count);
-        setSatellites(mStatus);
-        verifySatellites(mStatus);
-
-        GpsStatus otherStatus = createGpsStatus();
-        generateSatellitesData(count * 2);
-        setSatellites(otherStatus);
-        verifySatellites(otherStatus);
-
-        set(mStatus, otherStatus);
-        verifySatellites(mStatus);
-    }
-
-    public void testAddLessSatellites() throws Exception {
-        int count = 25;
-        generateSatellitesData(count * 2);
-        setSatellites(mStatus);
-        verifySatellites(mStatus);
-
-        GpsStatus otherStatus = createGpsStatus();
-        generateSatellitesData(count);
-        setSatellites(otherStatus);
-        verifySatellites(otherStatus);
-
-        set(mStatus, otherStatus);
-        verifySatellites(mStatus);
-    }
-
-    private static void verifyIsEmpty(GpsStatus status) {
-        verifySatelliteCount(status, 0);
-        verifyTtff(status, 0);
-    }
-
-    private static void verifySatelliteCount(GpsStatus status, int expectedCount) {
-        int satellites = 0;
-        for (GpsSatellite s : status.getSatellites()) {
-            ++satellites;
-        }
-        assertEquals("GpsStatus::SatelliteCount", expectedCount, satellites);
-    }
-
-    private void verifySatellites(GpsStatus status) {
-        verifySatelliteCount(status, mCount);
-        verifySatellites(status, mCount, mPrns, mCn0s, mElevations, mAzimuth, mEphemerisMask,
-                mAlmanacMask, mUsedInFixMask);
-    }
-
-    private static void verifySatellites(
-            GpsStatus status,
-            int count,
-            int[] prns,
-            float[] cn0s,
-            float[] elevations,
-            float[] azimuth,
-            int ephemerisMask,
-            int almanacMask,
-            int usedInFixMask) {
-        for (int i = 0; i < count; ++i) {
-            int prn = prns[i];
-            GpsSatellite satellite = getSatellite(status, prn);
-            assertNotNull(getSatelliteAssertInfo(i, prn, "non-null"), satellite);
-            assertEquals(getSatelliteAssertInfo(i, prn, "Snr"), cn0s[i], satellite.getSnr());
-            assertEquals(
-                    getSatelliteAssertInfo(i, prn, "Elevation"),
-                    elevations[i],
-                    satellite.getElevation());
-            assertEquals(
-                    getSatelliteAssertInfo(i, prn, "Azimuth"),
-                    azimuth[i],
-                    satellite.getAzimuth());
-            int prnShift = 1 << (prn - 1);
-            assertEquals(
-                    getSatelliteAssertInfo(i, prn, "ephemeris"),
-                    (ephemerisMask & prnShift) != 0,
-                    satellite.hasEphemeris());
-            assertEquals(
-                    getSatelliteAssertInfo(i, prn, "almanac"),
-                    (almanacMask & prnShift) != 0,
-                    satellite.hasAlmanac());
-            assertEquals(
-                    getSatelliteAssertInfo(i, prn, "usedInFix"),
-                    (usedInFixMask & prnShift) != 0,
-                    satellite.usedInFix());
-        }
-    }
-
-    private static void verifyTtff(GpsStatus status, int expectedTtff) {
-        assertEquals("GpsStatus::TTFF", expectedTtff, status.getTimeToFirstFix());
-    }
-
-    private static GpsStatus createGpsStatus() throws Exception {
-        Constructor<GpsStatus>  ctor = GpsStatus.class.getDeclaredConstructor();
-        ctor.setAccessible(true);
-        return ctor.newInstance();
-    }
-
-    private static void set(GpsStatus status, int ttff) throws Exception {
-        Class<?> statusClass = status.getClass();
-        Method setTtff = statusClass.getDeclaredMethod("setTimeToFirstFix", Integer.TYPE);
-        setTtff.setAccessible(true);
-        setTtff.invoke(status, ttff);
-    }
-
-    private static void set(GpsStatus status, GpsStatus statusToSet) throws Exception {
-        Class<?> statusClass = status.getClass();
-        Method setStatus = statusClass.getDeclaredMethod("setStatus", statusClass);
-        setStatus.setAccessible(true);
-        setStatus.invoke(status, statusToSet);
-    }
-
-    private void setSatellites(GpsStatus status) throws Exception {
-        set(status, mCount, mPrns, mCn0s, mElevations, mAzimuth, mEphemerisMask, mAlmanacMask,
-                mUsedInFixMask);
-    }
-
-    private static void set(
-            GpsStatus status,
-            int count,
-            int[] prns,
-            float[] cn0s,
-            float[] elevations,
-            float[] azimuth,
-            int ephemerisMask,
-            int almanacMask,
-            int usedInFixMask) throws Exception {
-        Class<?> statusClass = status.getClass();
-        Class<?> intClass = Integer.TYPE;
-        Class<?> floatArrayClass = Class.forName("[F");
-        Method setStatus = statusClass.getDeclaredMethod(
-                "setStatus",
-                intClass,
-                Class.forName("[I"),
-                floatArrayClass,
-                floatArrayClass,
-                floatArrayClass,
-                intClass,
-                intClass,
-                intClass);
-        setStatus.setAccessible(true);
-        setStatus.invoke(
-                status,
-                count,
-                prns,
-                cn0s,
-                elevations,
-                azimuth,
-                ephemerisMask,
-                almanacMask,
-                usedInFixMask);
-    }
-
-    private int generateInt() {
-        return mRandom.nextInt(MAX_VALUE) + 1;
-    }
-
-    private int[] generateIntArray(int count) {
-        Set<Integer> generatedPrns = new HashSet<>();
-        int[] array = new int[count];
-        for(int i = 0; i < count; ++i) {
-            int generated;
-            do {
-                generated = generateInt();
-            } while (generatedPrns.contains(generated));
-            array[i] = generated;
-            generatedPrns.add(generated);
-        }
-        return array;
-    }
-
-    private float[] generateFloatArray(int count) {
-        float[] array = new float[count];
-        for(int i = 0; i < count; ++i) {
-            array[i] = generateInt();
-        }
-        return array;
-    }
-
-    private int generateMask(int[] prns) {
-        int mask = 0;
-        int prnsLength = prns.length;
-        for (int i = 0; i < prnsLength; ++i) {
-            if (mRandom.nextBoolean()) {
-                mask |= 1 << (prns[i] - 1);
-            }
-        }
-        return mask;
-    }
-
-    private void generateSatellitesData(int count) {
-        generateSatellitesData(count, false /* reusePrns */);
-    }
-
-    private void generateSatellitesData(int count, boolean reusePrns) {
-        mCount = count;
-        if (!reusePrns) {
-            mPrns = generateIntArray(count);
-        }
-        mCn0s = generateFloatArray(count);
-        mElevations = generateFloatArray(count);
-        mAzimuth = generateFloatArray(count);
-        mEphemerisMask = generateMask(mPrns);
-        mAlmanacMask = generateMask(mPrns);
-        mUsedInFixMask = generateMask(mPrns);
-    }
-
-    private static GpsSatellite getSatellite(GpsStatus status, int prn) {
-        for (GpsSatellite satellite : status.getSatellites()) {
-            if (satellite.getPrn() == prn) {
-                return satellite;
-            }
-        }
-        return null;
-    }
-
-    private static String getSatelliteAssertInfo(int index, int prn, String param) {
-        return String.format("Satellite::%s [i=%d, prn=%d]", param, index, prn);
-    }
-}
diff --git a/location/tests/locationtests/src/android/location/SatelliteInfo.java b/location/tests/locationtests/src/android/location/SatelliteInfo.java
new file mode 100644
index 0000000..b6453ef
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/SatelliteInfo.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import java.util.List;
+
+/*
+ * Helper class to store single Satellite info, only used it in the unit test.
+ */
+public class SatelliteInfo {
+  private static final int SVID_MAX_BIT_INDEX = 32;
+  private static final int SVID_SHIFT_WIDTH = 8;
+  private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 4;
+
+  // Index for the bits in mSvidWithFlag
+  private static final int GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA_BIT_INDEX = 0;
+  private static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA_BIT_INDEX = 1;
+  private static final int GNSS_SV_FLAGS_USED_IN_FIX_BIT_INDEX = 2;
+  private static final int GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY_BIT_INDEX = 3;
+  public int mSvid;
+  public int mSvidWithFlag;
+  public float mCn0DbHz;
+  public float mElevation;
+  public float mAzimuth;
+  public float mCarrierFrequency;
+
+  /*
+   * Flag fields, it stores the same information as svidWithFlag, but in different format, easy for
+   * the unit test.
+   */
+  public int mConstellationType;
+  public boolean mHasEphemris;
+  public boolean mHasAlmanac;
+  public boolean mUsedInFix;
+  public boolean mHasCarriesFrequency;
+
+  public SatelliteInfo(int svid, int constellationType, boolean hasEphemris, boolean hasAlmanac,
+      boolean usedInFix, boolean hasCarriesFrequency, float cn0, float elevation, float azimuth,
+      float carrierFrequency) {
+    mSvidWithFlag =
+        setRange(mSvidWithFlag, constellationType, CONSTELLATION_TYPE_SHIFT_WIDTH, SVID_SHIFT_WIDTH);
+    mSvidWithFlag = setRange(mSvidWithFlag, svid, SVID_SHIFT_WIDTH, SVID_MAX_BIT_INDEX);
+    mSvidWithFlag = setBit(mSvidWithFlag, hasEphemris, GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA_BIT_INDEX);
+    mSvidWithFlag = setBit(mSvidWithFlag, hasAlmanac, GNSS_SV_FLAGS_HAS_ALMANAC_DATA_BIT_INDEX);
+    mSvidWithFlag = setBit(mSvidWithFlag, usedInFix, GNSS_SV_FLAGS_USED_IN_FIX_BIT_INDEX);
+    mSvidWithFlag =
+        setBit(mSvidWithFlag, hasCarriesFrequency, GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY_BIT_INDEX);
+    this.mSvid = svid;
+    this.mConstellationType = constellationType;
+    this.mCn0DbHz = cn0;
+    this.mElevation = elevation;
+    this.mAzimuth = azimuth;
+    this.mCarrierFrequency = carrierFrequency;
+    this.mHasEphemris = hasEphemris;
+    this.mHasAlmanac = hasAlmanac;
+    this.mUsedInFix = usedInFix;
+    this.mHasCarriesFrequency = hasCarriesFrequency;
+  }
+
+  /*
+   * Gernerate svidWithFlags array from svInfos
+   */
+  public static int[] getSvidWithFlagsArray(List<SatelliteInfo> svInfos) {
+    int[] svidWithFlags = new int[svInfos.size()];
+    for (int i = 0; i< svInfos.size(); i++) {
+      svidWithFlags[i] = svInfos.get(i).mSvidWithFlag;
+    }
+    return svidWithFlags;
+  }
+
+  /*
+   * Gernerate cn0s array from svInfos
+   */
+  public static float[] getCn0sArray(List<SatelliteInfo> svInfos) {
+    float[] cn0s = new float[svInfos.size()];
+    for (int i = 0; i< svInfos.size(); i++) {
+      cn0s[i] = svInfos.get(i).mCn0DbHz;
+    }
+    return cn0s;
+  }
+
+  /*
+   * Gernerate elevations array from svInfos
+   */
+  public static float[] getElevationsArray(List<SatelliteInfo> svInfos) {
+    float[] elevations = new float[svInfos.size()];
+    for (int i = 0; i< svInfos.size(); i++) {
+      elevations[i] = svInfos.get(i).mElevation;
+    }
+    return elevations;
+  }
+
+  /*
+   * Gernerate azimuths array from svInfos
+   */
+  public static float[] getAzimuthsArray(List<SatelliteInfo> svInfos) {
+    float[] azimuths = new float[svInfos.size()];
+    for (int i = 0; i< svInfos.size(); i++) {
+      azimuths[i] = svInfos.get(i).mAzimuth;
+    }
+    return azimuths;
+  }
+
+  /*
+   * Gernerate carrierFrequency array from svInfos
+   */
+  public static float[] getCarrierFrequencyArray(List<SatelliteInfo> svInfos) {
+    float[] carrierFrequencies = new float[svInfos.size()];
+    for (int i = 0; i< svInfos.size(); i++) {
+      carrierFrequencies[i] = svInfos.get(i).mCarrierFrequency;
+    }
+    return carrierFrequencies;
+  }
+
+  private int setBit(int targetValue, boolean value, int index) {
+    if (value) {
+      targetValue = targetValue | (1 << index);
+    } else {
+      targetValue = targetValue & ~(1 << index);
+    }
+    return targetValue;
+  }
+
+  /*
+   * Set the bit in the range [fromIndex, toIndex), index start from the lowest bit.
+   * value -> 1 1 0 1 1 0 1 0
+   * index -> 7 6 5 4 3 2 1 0
+   * This function will set the bit in the range to the lowest X bits of the value.
+   */
+  private int setRange(int targetValue, int value, int fromIndex, int toIndex) {
+    int rangeLen = toIndex - fromIndex;
+    int valueMask = (1 << rangeLen) -1;
+    value &= valueMask;
+    value = value << fromIndex;
+    valueMask = valueMask << fromIndex;
+    targetValue &= (~valueMask);
+    targetValue |= value;
+    return targetValue;
+  }
+
+}
\ No newline at end of file
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 1feea89..12e5744 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -91,10 +91,10 @@
  * are only decrypted when the samples are delivered to the decoder.
  * <p>
  * MediaDrm methods throw {@link android.media.MediaDrm.MediaDrmStateException}
- * when a method is called on a MediaDrm object that has had an unrecoverable failure 
- * in the DRM plugin or security hardware. 
- * {@link android.media.MediaDrm.MediaDrmStateException} extends 
- * {@link java.lang.IllegalStateException} with the addition of a developer-readable 
+ * when a method is called on a MediaDrm object that has had an unrecoverable failure
+ * in the DRM plugin or security hardware.
+ * {@link android.media.MediaDrm.MediaDrmStateException} extends
+ * {@link java.lang.IllegalStateException} with the addition of a developer-readable
  * diagnostic information string associated with the exception.
  * <p>
  * In the event of a mediaserver process crash or restart while a MediaDrm object
@@ -102,9 +102,9 @@
  * To recover, the app must release the MediaDrm object, then create and initialize
  * a new one.
  * <p>
- * As {@link android.media.MediaDrmResetException} and 
- * {@link android.media.MediaDrm.MediaDrmStateException} both extend 
- * {@link java.lang.IllegalStateException}, they should be in an earlier catch() 
+ * As {@link android.media.MediaDrmResetException} and
+ * {@link android.media.MediaDrm.MediaDrmStateException} both extend
+ * {@link java.lang.IllegalStateException}, they should be in an earlier catch()
  * block than {@link java.lang.IllegalStateException} if handled separately.
  * <p>
  * <a name="Callbacks"></a>
@@ -165,7 +165,7 @@
 
     /**
      * Query if the given scheme identified by its UUID is supported on
-     * this device, and whether the drm plugin is able to handle the
+     * this device, and whether the DRM plugin is able to handle the
      * media container format specified by mimeType.
      * @param uuid The UUID of the crypto scheme.
      * @param mimeType The MIME type of the media container, e.g. "video/mp4"
@@ -745,7 +745,7 @@
      * returned in KeyRequest.defaultUrl.
      * <p>
      * After the app has received the key request response from the server,
-     * it should deliver to the response to the DRM engine plugin using the method
+     * it should deliver to the response to the MediaDrm instance using the method
      * {@link #provideKeyResponse}.
      *
      * @param scope may be a sessionId or a keySetId, depending on the specified keyType.
@@ -781,7 +781,7 @@
 
     /**
      * A key response is received from the license server by the app, then it is
-     * provided to the DRM engine plugin using provideKeyResponse.  When the
+     * provided to the MediaDrm instance using provideKeyResponse.  When the
      * response is for an offline key request, a keySetId is returned that can be
      * used to later restore the keys to a new session with the method
      * {@link #restoreKeys}.
@@ -829,7 +829,7 @@
      * in the form of {name, value} pairs.  Since DRM license policies vary by vendor,
      * the specific status field names are determined by each DRM vendor.  Refer to your
      * DRM provider documentation for definitions of the field names for a particular
-     * DRM engine plugin.
+     * DRM plugin.
      *
      * @param sessionId the session ID for the DRM session
      */
@@ -897,11 +897,11 @@
            @NonNull String certAuthority);
 
     /**
-     * After a provision response is received by the app, it is provided to the DRM
-     * engine plugin using this method.
+     * After a provision response is received by the app, it is provided to the
+     * MediaDrm instance using this method.
      *
      * @param response the opaque provisioning response byte array to provide to the
-     * DRM engine plugin.
+     * MediaDrm instance.
      *
      * @throws DeniedByServerException if the response indicates that the
      * server rejected the request
@@ -912,7 +912,6 @@
     }
 
     @NonNull
-    /* could there be a valid response with 0-sized certificate or key? */
     private native Certificate provideProvisionResponseNative(@NonNull byte[] response)
             throws DeniedByServerException;
 
@@ -953,26 +952,26 @@
     /**
      * Remove all secure stops without requiring interaction with the server.
      */
-     public native void releaseAllSecureStops();
+    public native void releaseAllSecureStops();
 
     /**
-     * String property name: identifies the maker of the DRM engine plugin
+     * String property name: identifies the maker of the DRM plugin
      */
     public static final String PROPERTY_VENDOR = "vendor";
 
     /**
-     * String property name: identifies the version of the DRM engine plugin
+     * String property name: identifies the version of the DRM plugin
      */
     public static final String PROPERTY_VERSION = "version";
 
     /**
-     * String property name: describes the DRM engine plugin
+     * String property name: describes the DRM plugin
      */
     public static final String PROPERTY_DESCRIPTION = "description";
 
     /**
      * String property name: a comma-separated list of cipher and mac algorithms
-     * supported by CryptoSession.  The list may be empty if the DRM engine
+     * supported by CryptoSession.  The list may be empty if the DRM
      * plugin does not support CryptoSession operations.
      */
     public static final String PROPERTY_ALGORITHMS = "algorithms";
@@ -988,7 +987,7 @@
     public @interface StringProperty {}
 
     /**
-     * Read a DRM engine plugin String property value, given the property name string.
+     * Read a MediaDrm String property value, given the property name string.
      * <p>
      * Standard fields names are:
      * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION},
@@ -998,6 +997,13 @@
     public native String getPropertyString(@NonNull @StringProperty String propertyName);
 
     /**
+     * Set a MediaDrm String property value, given the property name string
+     * and new value for the property.
+     */
+    public native void setPropertyString(@NonNull @StringProperty String propertyName,
+            @NonNull String value);
+
+    /**
      * Byte array property name: the device unique identifier is established during
      * device provisioning and provides a means of uniquely identifying each device.
      */
@@ -1011,7 +1017,7 @@
     public @interface ArrayProperty {}
 
     /**
-     * Read a DRM engine plugin byte array property value, given the property name string.
+     * Read a MediaDrm byte array property value, given the property name string.
      * <p>
      * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID}
      */
@@ -1019,17 +1025,13 @@
     public native byte[] getPropertyByteArray(@ArrayProperty String propertyName);
 
     /**
-     * Set a DRM engine plugin String property value.
-     */
-    public native void setPropertyString(
-            String propertyName, @NonNull String value);
-
-    /**
-     * Set a DRM engine plugin byte array property value.
-     */
-    public native void setPropertyByteArray(
+    * Set a MediaDrm byte array property value, given the property name string
+    * and new value for the property.
+    */
+    public native void setPropertyByteArray(@NonNull @ArrayProperty
             String propertyName, @NonNull byte[] value);
 
+
     private static final native void setCipherAlgorithmNative(
             @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
 
@@ -1158,7 +1160,7 @@
      * The algorithm string conforms to JCA Standard Names for Mac
      * Algorithms and is case insensitive.  For example "HmacSHA256".
      * <p>
-     * The list of supported algorithms for a DRM engine plugin can be obtained
+     * The list of supported algorithms for a DRM plugin can be obtained
      * using the method {@link #getPropertyString} with the property name
      * "algorithms".
      */
@@ -1272,7 +1274,7 @@
      * storage, and used when invoking the signRSA method.
      *
      * @param response the opaque certificate response byte array to provide to the
-     * DRM engine plugin.
+     * MediaDrm instance.
      *
      * @throws DeniedByServerException if the response indicates that the
      * server rejected the request
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 4bb16ff..26fd750 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -732,6 +732,11 @@
     <!-- UI debug setting: profile time taken by hardware acceleration to render apps [CHAR LIMIT=25] -->
     <string name="track_frame_time">Profile GPU rendering</string>
 
+    <!-- UI debug setting: enable gpu debug layers [CHAR LIMIT=25] -->
+    <string name="enable_gpu_debug_layers">Enable GPU debug layers</string>
+    <!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] -->
+    <string name="enable_gpu_debug_layers_summary">Allow loading GPU debug layers for debug apps</string>
+
     <!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] -->
     <string name="window_animation_scale_title">Window animation scale</string>
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 41b205b..ba1c9e3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -869,6 +869,15 @@
         dumpSetting(s, p,
                 Settings.Global.WAIT_FOR_DEBUGGER,
                 GlobalSettingsProto.WAIT_FOR_DEBUGGER);
+        dumpSetting(s, p,
+                Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
+                GlobalSettingsProto.ENABLE_GPU_DEBUG_LAYERS);
+        dumpSetting(s, p,
+                Settings.Global.GPU_DEBUG_APP,
+                GlobalSettingsProto.GPU_DEBUG_APP);
+        dumpSetting(s, p,
+                Settings.Global.GPU_DEBUG_LAYERS,
+                GlobalSettingsProto.GPU_DEBUG_LAYERS);
         // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Global.LOW_POWER_MODE,
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 701c574..3cd2f6a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -58,6 +58,7 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.ActivityManagerService.NeededUriGrants;
+import com.android.server.am.proto.ActiveServicesProto;
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -85,6 +86,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 import android.webkit.WebViewZygote;
 
 public final class ActiveServices {
@@ -633,7 +635,7 @@
                         sb.append("Stopping service due to app idle: ");
                         UserHandle.formatUid(sb, service.appInfo.uid);
                         sb.append(" ");
-                        TimeUtils.formatDuration(service.createTime
+                        TimeUtils.formatDuration(service.createRealTime
                                 - SystemClock.elapsedRealtime(), sb);
                         sb.append(" ");
                         sb.append(compName);
@@ -3220,7 +3222,7 @@
         info.uid = r.appInfo.uid;
         info.process = r.processName;
         info.foreground = r.isForeground;
-        info.activeSince = r.createTime;
+        info.activeSince = r.createRealTime;
         info.started = r.startRequested;
         info.clientCount = r.connections.size();
         info.crashCount = r.crashCount;
@@ -3574,7 +3576,7 @@
                 pw.print("    app=");
                 pw.println(r.app);
                 pw.print("    created=");
-                TimeUtils.formatDuration(r.createTime, nowReal, pw);
+                TimeUtils.formatDuration(r.createRealTime, nowReal, pw);
                 pw.print(" started=");
                 pw.print(r.startRequested);
                 pw.print(" connections=");
@@ -3840,6 +3842,26 @@
         return new ServiceDumper(fd, pw, args, opti, dumpAll, dumpPackage);
     }
 
+    protected void writeToProto(ProtoOutputStream proto) {
+        synchronized (mAm) {
+            int[] users = mAm.mUserController.getUsers();
+            for (int user : users) {
+                ServiceMap smap = mServiceMap.get(user);
+                if (smap == null) {
+                    continue;
+                }
+                long token = proto.start(ActiveServicesProto.SERVICES_BY_USERS);
+                proto.write(ActiveServicesProto.ServicesByUser.USER_ID, user);
+                ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
+                for (int i=0; i<alls.size(); i++) {
+                    alls.valueAt(i).writeToProto(proto,
+                            ActiveServicesProto.ServicesByUser.SERVICE_RECORDS);
+                }
+                proto.end(token);
+            }
+        }
+    }
+
     /**
      * There are three ways to call this:
      *  - no service specified: dump all the services
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fe5c789..b3b831e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -415,6 +415,8 @@
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.am.proto.ActivityManagerServiceProto;
 import com.android.server.am.proto.BroadcastProto;
+import com.android.server.am.proto.GrantUriProto;
+import com.android.server.am.proto.NeededUriGrantsProto;
 import com.android.server.am.proto.StickyBroadcastProto;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
@@ -1191,6 +1193,13 @@
             return result;
         }
 
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            long token = proto.start(fieldId);
+            proto.write(GrantUriProto.URI, uri.toString());
+            proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
+            proto.end(token);
+        }
+
         public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
             return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
                     ContentProvider.getUriWithoutUserId(uri), false);
@@ -9044,6 +9053,19 @@
             this.targetUid = targetUid;
             this.flags = flags;
         }
+
+        void writeToProto(ProtoOutputStream proto, long fieldId) {
+            long token = proto.start(fieldId);
+            proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
+            proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
+            proto.write(NeededUriGrantsProto.FLAGS, flags);
+
+            final int N = this.size();
+            for (int i=0; i<N; i++) {
+                this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS);
+            }
+            proto.end(token);
+        }
     }
 
     /**
@@ -14939,6 +14961,8 @@
                     pw.println("No providers match: " + name);
                     pw.println("Use -h for help.");
                 }
+            } else if ("service".equals(cmd)) {
+                mServices.writeToProto(proto);
             } else {
                 // default option, dump everything, output is ActivityManagerServiceProto
                 synchronized (this) {
@@ -14949,6 +14973,10 @@
                     long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
                     writeBroadcastsToProtoLocked(proto);
                     proto.end(broadcastToken);
+
+                    long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
+                    mServices.writeToProto(proto);
+                    proto.end(serviceToken);
                 }
             }
             proto.flush();
diff --git a/services/core/java/com/android/server/am/AppBindRecord.java b/services/core/java/com/android/server/am/AppBindRecord.java
index df833ad..7b38597 100644
--- a/services/core/java/com/android/server/am/AppBindRecord.java
+++ b/services/core/java/com/android/server/am/AppBindRecord.java
@@ -17,6 +17,9 @@
 package com.android.server.am;
 
 import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.AppBindRecordProto;
 
 import java.io.PrintWriter;
 
@@ -60,4 +63,18 @@
             + Integer.toHexString(System.identityHashCode(this))
             + " " + service.shortName + ":" + client.processName + "}";
     }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(AppBindRecordProto.HEX_HASH,
+                Integer.toHexString(System.identityHashCode(this)));
+        if (client != null) {
+            client.writeToProto(proto, AppBindRecordProto.CLIENT);
+        }
+        final int N = connections.size();
+        for (int i=0; i<N; i++) {
+            connections.valueAt(i).writeToProto(proto, AppBindRecordProto.CONNECTIONS);
+        }
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 9b7a0c4..6df283c 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -19,6 +19,9 @@
 import android.app.IServiceConnection;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.ConnectionRecordProto;
 
 import java.io.PrintWriter;
 
@@ -119,4 +122,70 @@
         sb.append('}');
         return stringName = sb.toString();
     }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        if (binding == null) return; // if binding is null, don't write data, something is wrong.
+        long token = proto.start(fieldId);
+        proto.write(ConnectionRecordProto.HEX_HASH,
+                Integer.toHexString(System.identityHashCode(this)));
+        if (binding.client != null) {
+            proto.write(ConnectionRecordProto.USER_ID, binding.client.userId);
+        }
+        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.AUTO_CREATE);
+        }
+        if ((flags&Context.BIND_DEBUG_UNBIND) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEBUG_UNBIND);
+        }
+        if ((flags&Context.BIND_NOT_FOREGROUND) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_FG);
+        }
+        if ((flags&Context.BIND_IMPORTANT_BACKGROUND) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT_BG);
+        }
+        if ((flags&Context.BIND_ABOVE_CLIENT) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ABOVE_CLIENT);
+        }
+        if ((flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ALLOW_OOM_MANAGEMENT);
+        }
+        if ((flags&Context.BIND_WAIVE_PRIORITY) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.WAIVE_PRIORITY);
+        }
+        if ((flags&Context.BIND_IMPORTANT) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT);
+        }
+        if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ADJUST_WITH_ACTIVITY);
+        }
+        if ((flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE_WHILE_WAKE);
+        }
+        if ((flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE);
+        }
+        if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.TREAT_LIKE_ACTIVITY);
+        }
+        if ((flags&Context.BIND_VISIBLE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.VISIBLE);
+        }
+        if ((flags&Context.BIND_SHOWING_UI) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.SHOWING_UI);
+        }
+        if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_VISIBLE);
+        }
+        if (serviceDead) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEAD);
+        }
+        if (binding.service != null) {
+            proto.write(ConnectionRecordProto.SERVICE_NAME, binding.service.shortName);
+        }
+        if (conn != null) {
+            proto.write(ConnectionRecordProto.CONN_HEX_HASH,
+                    Integer.toHexString(System.identityHashCode(conn.asBinder())));
+        }
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index be290e9..01ce64c 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -21,6 +21,10 @@
 import android.os.IBinder;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.AppBindRecordProto;
+import com.android.server.am.proto.IntentBindRecordProto;
 
 import java.io.PrintWriter;
 
@@ -106,4 +110,32 @@
         sb.append('}');
         return stringName = sb.toString();
     }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(IntentBindRecordProto.HEX_HASH,
+                Integer.toHexString(System.identityHashCode(this)));
+        proto.write(IntentBindRecordProto.IS_CREATE,
+                (collectFlags()&Context.BIND_AUTO_CREATE) != 0);
+        if (intent != null) {
+            intent.getIntent().writeToProto(proto,
+                    IntentBindRecordProto.INTENT, false, true, false, false);
+        }
+        if (binder != null) {
+            proto.write(IntentBindRecordProto.BINDER, binder.toString());
+        }
+        proto.write(IntentBindRecordProto.REQUESTED, requested);
+        proto.write(IntentBindRecordProto.RECEIVED, received);
+        proto.write(IntentBindRecordProto.HAS_BOUND, hasBound);
+        proto.write(IntentBindRecordProto.DO_REBIND, doRebind);
+
+        final int N = apps.size();
+        for (int i=0; i<N; i++) {
+            AppBindRecord a = apps.valueAt(i);
+            if (a != null) {
+                a.writeToProto(proto, IntentBindRecordProto.APPS);
+            }
+        }
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 16995e5..b6eff00 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -19,6 +19,7 @@
 import com.android.internal.app.procstats.ServiceState;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.LocalServices;
+import com.android.server.am.proto.ServiceRecordProto;
 import com.android.server.notification.NotificationManagerInternal;
 
 import android.app.INotificationManager;
@@ -42,6 +43,8 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -79,7 +82,7 @@
     final String permission;// permission needed to access service
     final boolean exported; // from ServiceInfo.exported
     final Runnable restarter; // used to schedule retries of starting the service
-    final long createTime;  // when this service was created
+    final long createRealTime;  // when this service was created
     final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
             = new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
                             // All active bindings to the service.
@@ -103,7 +106,7 @@
     boolean startRequested; // someone explicitly called start?
     boolean delayedStop;    // service has been stopped but is in a delayed start?
     boolean stopIfKilled;   // last onStart() said to stop if service killed?
-    boolean callStart;      // last onStart() has asked to alway be called on restart.
+    boolean callStart;      // last onStart() has asked to always be called on restart.
     int executeNesting;     // number of outstanding operations keeping foreground.
     boolean executeFg;      // should we be executing in the foreground?
     long executingStart;    // start time of last execute request.
@@ -159,6 +162,27 @@
             }
         }
 
+        public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
+            long token = proto.start(fieldId);
+            proto.write(ServiceRecordProto.StartItemProto.ID, id);
+            ProtoUtils.toDuration(proto,
+                    ServiceRecordProto.StartItemProto.DURATION, deliveredTime, now);
+            proto.write(ServiceRecordProto.StartItemProto.DELIVERY_COUNT, deliveryCount);
+            proto.write(ServiceRecordProto.StartItemProto.DONE_EXECUTING_COUNT, doneExecutingCount);
+            if (intent != null) {
+                intent.writeToProto(proto, ServiceRecordProto.StartItemProto.INTENT, true, true,
+                        true, false);
+            }
+            if (neededGrants != null) {
+                neededGrants.writeToProto(proto, ServiceRecordProto.StartItemProto.NEEDED_GRANTS);
+            }
+            if (uriPermissions != null) {
+                uriPermissions.writeToProto(proto,
+                        ServiceRecordProto.StartItemProto.URI_PERMISSIONS);
+            }
+            proto.end(token);
+        }
+
         public String toString() {
             if (stringName != null) {
                 return stringName;
@@ -209,6 +233,117 @@
         }
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(ServiceRecordProto.SHORT_NAME, this.shortName);
+        proto.write(ServiceRecordProto.HEX_HASH,
+                Integer.toHexString(System.identityHashCode(this)));
+        proto.write(ServiceRecordProto.IS_RUNNING, app != null);
+        if (app != null) {
+            proto.write(ServiceRecordProto.PID, app.pid);
+        }
+        if (intent != null) {
+            intent.getIntent().writeToProto(proto, ServiceRecordProto.INTENT, false, true, false,
+                    true);
+        }
+        proto.write(ServiceRecordProto.PACKAGE_NAME, packageName);
+        proto.write(ServiceRecordProto.PROCESS_NAME, processName);
+        proto.write(ServiceRecordProto.PERMISSION, permission);
+
+        long now = SystemClock.uptimeMillis();
+        long nowReal = SystemClock.elapsedRealtime();
+        if (appInfo != null) {
+            long appInfoToken = proto.start(ServiceRecordProto.APPINFO);
+            proto.write(ServiceRecordProto.AppInfo.BASE_DIR, appInfo.sourceDir);
+            if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+                proto.write(ServiceRecordProto.AppInfo.RES_DIR, appInfo.publicSourceDir);
+            }
+            proto.write(ServiceRecordProto.AppInfo.DATA_DIR, appInfo.dataDir);
+            proto.end(appInfoToken);
+        }
+        if (app != null) {
+            app.writeToProto(proto, ServiceRecordProto.APP);
+        }
+        if (isolatedProc != null) {
+            isolatedProc.writeToProto(proto, ServiceRecordProto.ISOLATED_PROC);
+        }
+        proto.write(ServiceRecordProto.WHITELIST_MANAGER, whitelistManager);
+        proto.write(ServiceRecordProto.DELAYED, delayed);
+        if (isForeground || foregroundId != 0) {
+            long fgToken = proto.start(ServiceRecordProto.FOREGROUND);
+            proto.write(ServiceRecordProto.Foreground.ID, foregroundId);
+            foregroundNoti.writeToProto(proto, ServiceRecordProto.Foreground.NOTIFICATION);
+            proto.end(fgToken);
+        }
+        ProtoUtils.toDuration(proto, ServiceRecordProto.CREATE_REAL_TIME, createRealTime, nowReal);
+        ProtoUtils.toDuration(proto,
+                ServiceRecordProto.STARTING_BG_TIMEOUT, startingBgTimeout, now);
+        ProtoUtils.toDuration(proto, ServiceRecordProto.LAST_ACTIVITY_TIME, lastActivity, now);
+        ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now);
+        proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg);
+
+        if (startRequested || delayedStop || lastStartId != 0) {
+            long startToken = proto.start(ServiceRecordProto.START);
+            proto.write(ServiceRecordProto.Start.START_REQUESTED, startRequested);
+            proto.write(ServiceRecordProto.Start.DELAYED_STOP, delayedStop);
+            proto.write(ServiceRecordProto.Start.STOP_IF_KILLED, stopIfKilled);
+            proto.write(ServiceRecordProto.Start.LAST_START_ID, lastStartId);
+            proto.end(startToken);
+        }
+
+        if (executeNesting != 0) {
+            long executNestingToken = proto.start(ServiceRecordProto.EXECUTE);
+            proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_NESTING, executeNesting);
+            proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_FG, executeFg);
+            ProtoUtils.toDuration(proto,
+                    ServiceRecordProto.ExecuteNesting.EXECUTING_START, executingStart, now);
+            proto.end(executNestingToken);
+        }
+        if (destroying || destroyTime != 0) {
+            ProtoUtils.toDuration(proto, ServiceRecordProto.DESTORY_TIME, destroyTime, now);
+        }
+        if (crashCount != 0 || restartCount != 0 || restartDelay != 0 || nextRestartTime != 0) {
+            long crashToken = proto.start(ServiceRecordProto.CRASH);
+            proto.write(ServiceRecordProto.Crash.RESTART_COUNT, restartCount);
+            ProtoUtils.toDuration(proto, ServiceRecordProto.Crash.RESTART_DELAY, restartDelay, now);
+            ProtoUtils.toDuration(proto,
+                    ServiceRecordProto.Crash.NEXT_RESTART_TIME, nextRestartTime, now);
+            proto.write(ServiceRecordProto.Crash.CRASH_COUNT, crashCount);
+            proto.end(crashToken);
+        }
+
+        if (deliveredStarts.size() > 0) {
+            final int N = deliveredStarts.size();
+            for (int i = 0; i < N; i++) {
+                deliveredStarts.get(i).writeToProto(proto,
+                        ServiceRecordProto.DELIVERED_STARTS, now);
+            }
+        }
+        if (pendingStarts.size() > 0) {
+            final int N = pendingStarts.size();
+            for (int i = 0; i < N; i++) {
+                pendingStarts.get(i).writeToProto(proto, ServiceRecordProto.PENDING_STARTS, now);
+            }
+        }
+        if (bindings.size() > 0) {
+            final int N = bindings.size();
+            for (int i=0; i<N; i++) {
+                IntentBindRecord b = bindings.valueAt(i);
+                b.writeToProto(proto, ServiceRecordProto.BINDINGS);
+            }
+        }
+        if (connections.size() > 0) {
+            final int N = connections.size();
+            for (int conni=0; conni<N; conni++) {
+                ArrayList<ConnectionRecord> c = connections.valueAt(conni);
+                for (int i=0; i<c.size(); i++) {
+                    c.get(i).writeToProto(proto, ServiceRecordProto.CONNECTIONS);
+                }
+            }
+        }
+        proto.end(token);
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("intent={");
                 pw.print(intent.getIntent().toShortString(false, true, false, true));
@@ -243,7 +378,7 @@
                     pw.print(" foregroundNoti="); pw.println(foregroundNoti);
         }
         pw.print(prefix); pw.print("createTime=");
-                TimeUtils.formatDuration(createTime, nowReal, pw);
+                TimeUtils.formatDuration(createRealTime, nowReal, pw);
                 pw.print(" startingBgTimeout=");
                 TimeUtils.formatDuration(startingBgTimeout, now, pw);
                 pw.println();
@@ -329,7 +464,7 @@
         permission = sInfo.permission;
         exported = sInfo.exported;
         this.restarter = restarter;
-        createTime = SystemClock.elapsedRealtime();
+        createRealTime = SystemClock.elapsedRealtime();
         lastActivity = SystemClock.uptimeMillis();
         userId = UserHandle.getUserId(appInfo.uid);
         createdFromFg = callerIsFg;
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java
index 28344df..fc07c1a 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/am/UriPermissionOwner.java
@@ -20,6 +20,9 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.UriPermissionOwnerProto;
 
 import com.google.android.collect.Sets;
 
@@ -139,6 +142,26 @@
         }
     }
 
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(UriPermissionOwnerProto.OWNER, owner.toString());
+        if (mReadPerms != null) {
+            synchronized (mReadPerms) {
+                for (UriPermission p : mReadPerms) {
+                    p.uri.writeToProto(proto, UriPermissionOwnerProto.READ_PERMS);
+                }
+            }
+        }
+        if (mWritePerms != null) {
+            synchronized (mWritePerms) {
+                for (UriPermission p : mWritePerms) {
+                    p.uri.writeToProto(proto, UriPermissionOwnerProto.WRITE_PERMS);
+                }
+            }
+        }
+        proto.end(token);
+    }
+
     @Override
     public String toString() {
         return owner.toString();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1372f2b..6ba1d8d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -736,6 +736,11 @@
                 for (NotificationVisibility nv : newlyVisibleKeys) {
                     NotificationRecord r = mNotificationsByKey.get(nv.key);
                     if (r == null) continue;
+                    if (!r.isSeen()) {
+                        // Report to usage stats that notification was made visible
+                        if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
+                        reportSeen(r);
+                    }
                     r.setVisibility(true, nv.rank);
                     nv.recycle();
                 }
@@ -1643,6 +1648,14 @@
         return INotificationManager.Stub.asInterface(mService);
     }
 
+    protected void reportSeen(NotificationRecord r) {
+        final int userId = r.sbn.getUserId();
+        mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+                userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
+                        : userId,
+                UsageEvents.Event.NOTIFICATION_SEEN);
+    }
+
     @VisibleForTesting
     NotificationManagerInternal getInternalService() {
         return mInternalService;
@@ -2269,10 +2282,7 @@
                             }
                             if (!r.isSeen()) {
                                 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
-                                mAppUsageStats.reportEvent(r.sbn.getPackageName(),
-                                        userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
-                                                : userId,
-                                        UsageEvents.Event.USER_INTERACTION);
+                                reportSeen(r);
                                 r.setSeen();
                             }
                         }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 679250c..8591304 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
 
+import android.annotation.Nullable;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -40,6 +41,7 @@
 import com.android.server.pm.dex.DexoptOptions;
 
 import java.io.File;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.TimeUnit;
@@ -402,14 +404,22 @@
     }
 
     /**
-     * Execute the idle optimizations immediately.
+     * Execute idle optimizations immediately on packages in packageNames. If packageNames is null,
+     * then execute on all packages.
      */
-    public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context) {
+    public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context,
+            @Nullable List<String> packageNames) {
         // Create a new object to make sure we don't interfere with the scheduled jobs.
         // Note that this may still run at the same time with the job scheduled by the
         // JobScheduler but the scheduler will not be able to cancel it.
         BackgroundDexOptService bdos = new BackgroundDexOptService();
-        int result = bdos.idleOptimization(pm, pm.getOptimizablePackages(), context);
+        ArraySet<String> packagesToOptimize;
+        if (packageNames == null) {
+            packagesToOptimize = pm.getOptimizablePackages();
+        } else {
+            packagesToOptimize = new ArraySet<>(packageNames);
+        }
+        int result = bdos.idleOptimization(pm, packagesToOptimize, context);
         return result == OPTIMIZE_PROCESSED;
     }
 
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 210eb13..6a06be2 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -486,6 +486,16 @@
         }
     }
 
+    public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
+            @Nullable String volumeUuid, int flags) throws InstallerException {
+        if (!checkBeforeRemote()) return new byte[0];
+        try {
+            return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     public void invalidateMounts() throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 83cffe5..bbe59eb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -293,6 +293,7 @@
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
+import com.android.server.pm.dex.DexLogger;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.PackageDexUsage;
@@ -2380,7 +2381,10 @@
 
         mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                 "*dexopt*");
-        mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
+        DexManager.Listener dexManagerListener = DexLogger.getListener(this,
+                installer, mInstallLock);
+        mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
+                dexManagerListener);
         mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
 
         mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -8985,11 +8989,11 @@
      * Execute the background dexopt job immediately.
      */
     @Override
-    public boolean runBackgroundDexoptJob() {
+    public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) {
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return false;
         }
-        return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext);
+        return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
     }
 
     List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 807eb1a..ee773a5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1219,7 +1219,13 @@
     }
 
     private int runDexoptJob() throws RemoteException {
-        boolean result = mInterface.runBackgroundDexoptJob();
+        String arg;
+        List<String> packageNames = new ArrayList<>();
+        while ((arg = getNextArg()) != null) {
+            packageNames.add(arg);
+        }
+        boolean result = mInterface.runBackgroundDexoptJob(packageNames.isEmpty() ? null :
+                packageNames);
         return result ? 0 : -1;
     }
 
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
new file mode 100644
index 0000000..c7bbf1c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+
+import android.util.ArraySet;
+import android.util.ByteStringUtils;
+import android.util.EventLog;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+
+import java.io.File;
+import java.util.Set;
+
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+
+/**
+ * This class is responsible for logging data about secondary dex files.
+ * The data logged includes hashes of the name and content of each file.
+ */
+public class DexLogger implements DexManager.Listener {
+    private static final String TAG = "DexLogger";
+
+    // Event log tag & subtag used for SafetyNet logging of dynamic
+    // code loading (DCL) - see b/63927552.
+    private static final int SNET_TAG = 0x534e4554;
+    private static final String DCL_SUBTAG = "dcl";
+
+    private final IPackageManager mPackageManager;
+    private final Object mInstallLock;
+    @GuardedBy("mInstallLock")
+    private final Installer mInstaller;
+
+    public static DexManager.Listener getListener(IPackageManager pms,
+            Installer installer, Object installLock) {
+        return new DexLogger(pms, installer, installLock);
+    }
+
+    private DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+      mPackageManager = pms;
+      mInstaller = installer;
+      mInstallLock = installLock;
+    }
+
+    /**
+     * Compute and log hashes of the name and content of a secondary dex file.
+     */
+    @Override
+    public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+            String dexPath, int storageFlags) {
+        int ownerUid = appInfo.uid;
+
+        byte[] hash = null;
+        synchronized(mInstallLock) {
+            try {
+                hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
+                        ownerUid, appInfo.volumeUuid, storageFlags);
+            } catch (InstallerException e) {
+                Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
+                        " : " + e.getMessage());
+            }
+        }
+        if (hash == null) {
+            return;
+        }
+
+        String dexFileName = new File(dexPath).getName();
+        String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
+        // Valid SHA256 will be 256 bits, 32 bytes.
+        if (hash.length == 32) {
+            message = message + ' ' + ByteStringUtils.toHexString(hash);
+        }
+
+        EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, ownerUid, message);
+
+        if (dexUseInfo.isUsedByOtherApps()) {
+            Set<String> otherPackages = dexUseInfo.getLoadingPackages();
+            Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
+            for (String otherPackageName : otherPackages) {
+                try {
+                    int otherUid = mPackageManager.getPackageUid(
+                        otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
+                    if (otherUid != -1 && otherUid != ownerUid) {
+                        otherUids.add(otherUid);
+                    }
+                } catch (RemoteException ignore) {
+                    // Can't happen, we're local.
+                }
+            }
+            for (int otherUid : otherUids) {
+                EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, otherUid, message);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6274754..0e2730c 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -76,6 +76,7 @@
     private final Object mInstallLock;
     @GuardedBy("mInstallLock")
     private final Installer mInstaller;
+    private final Listener mListener;
 
     // Possible outcomes of a dex search.
     private static int DEX_SEARCH_NOT_FOUND = 0;  // dex file not found
@@ -96,14 +97,24 @@
      */
     private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
 
+    public interface Listener {
+        /**
+         * Invoked just before the secondary dex file {@code dexPath} for the specified application
+         * is reconciled.
+         */
+        void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+                String dexPath, int storageFlags);
+    }
+
     public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
-            Installer installer, Object installLock) {
+            Installer installer, Object installLock, Listener listener) {
       mPackageCodeLocationsCache = new HashMap<>();
       mPackageDexUsage = new PackageDexUsage();
       mPackageManager = pms;
       mPackageDexOptimizer = pdo;
       mInstaller = installer;
       mInstallLock = installLock;
+      mListener = listener;
     }
 
     /**
@@ -389,7 +400,7 @@
                 : mPackageDexOptimizer;
         String packageName = options.getPackageName();
         PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
-        if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+        if (useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
                 Slog.d(TAG, "No secondary dex use for package:" + packageName);
             }
@@ -433,7 +444,7 @@
      */
     public void reconcileSecondaryDexFiles(String packageName) {
         PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
-        if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+        if (useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
                 Slog.d(TAG, "No secondary dex use for package:" + packageName);
             }
@@ -481,12 +492,16 @@
                 continue;
             }
 
+            if (mListener != null) {
+                mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
+            }
+
             boolean dexStillExists = true;
             synchronized(mInstallLock) {
                 try {
                     String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
                     dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
-                            pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
+                            info.uid, isas, info.volumeUuid, flags);
                 } catch (InstallerException e) {
                     Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
                             " : " + e.getMessage());
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 2fceae4..3992f8a 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -35,10 +35,15 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Class to decide whether to turn on battery saver mode for specific service
  *
+ * TODO: We should probably make {@link #mFilesForInteractive} and {@link #mFilesForNoninteractive}
+ * less flexible and just take a list of "CPU number - frequency" pairs. Being able to write
+ * anything under /sys/ and /proc/ is too loose.
+ *
  * Test: atest BatterySaverPolicyTest
  */
 public class BatterySaverPolicy extends ContentObserver {
@@ -65,8 +70,8 @@
     private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
     private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
 
-    private static final String KEY_SCREEN_ON_FILE_PREFIX = "file-on:";
-    private static final String KEY_SCREEN_OFF_FILE_PREFIX = "file-off:";
+    private static final String KEY_FILE_FOR_INTERACTIVE_PREFIX = "file-on:";
+    private static final String KEY_FILE_FOR_NONINTERACTIVE_PREFIX = "file-off:";
 
     private static String mSettings;
     private static String mDeviceSpecificSettings;
@@ -173,25 +178,25 @@
     private ContentResolver mContentResolver;
 
     @GuardedBy("mLock")
-    private final ArrayList<BatterySaverPolicyListener> mListeners = new ArrayList<>();
+    private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>();
 
     /**
      * List of [Filename -> content] that should be written when battery saver is activated
-     * and the screen is on.
+     * and the device is interactive.
      *
      * We use this to change the max CPU frequencies.
      */
     @GuardedBy("mLock")
-    private ArrayMap<String, String> mScreenOnFiles;
+    private ArrayMap<String, String> mFilesForInteractive;
 
     /**
      * List of [Filename -> content] that should be written when battery saver is activated
-     * and the screen is off.
+     * and the device is non-interactive.
      *
      * We use this to change the max CPU frequencies.
      */
     @GuardedBy("mLock")
-    private ArrayMap<String, String> mScreenOffFiles;
+    private ArrayMap<String, String> mFilesForNoninteractive;
 
     public interface BatterySaverPolicyListener {
         void onBatterySaverPolicyChanged(BatterySaverPolicy policy);
@@ -230,11 +235,6 @@
         return R.string.config_batterySaverDeviceSpecificConfig;
     }
 
-    @VisibleForTesting
-    void onChangeForTest() {
-        onChange(true, null);
-    }
-
     @Override
     public void onChange(boolean selfChange, Uri uri) {
         final BatterySaverPolicyListener[] listeners;
@@ -307,8 +307,8 @@
                     + deviceSpecificSetting);
         }
 
-        mScreenOnFiles = collectParams(parser, KEY_SCREEN_ON_FILE_PREFIX);
-        mScreenOffFiles = collectParams(parser, KEY_SCREEN_OFF_FILE_PREFIX);
+        mFilesForInteractive = collectParams(parser, KEY_FILE_FOR_INTERACTIVE_PREFIX);
+        mFilesForNoninteractive = collectParams(parser, KEY_FILE_FOR_NONINTERACTIVE_PREFIX);
     }
 
     private static ArrayMap<String, String> collectParams(
@@ -322,7 +322,7 @@
             }
             final String path = key.substring(prefix.length());
 
-            if (!(path.startsWith("/sys/") || path.startsWith("/proc"))) {
+            if (!(path.startsWith("/sys/") || path.startsWith("/proc/"))) {
                 Slog.wtf(TAG, "Invalid path: " + path);
                 continue;
             }
@@ -389,9 +389,9 @@
         }
     }
 
-    public ArrayMap<String, String> getFileValues(boolean screenOn) {
+    public ArrayMap<String, String> getFileValues(boolean interactive) {
         synchronized (mLock) {
-            return screenOn ? mScreenOnFiles : mScreenOffFiles;
+            return interactive ? mFilesForInteractive : mFilesForNoninteractive;
         }
     }
 
@@ -418,12 +418,12 @@
             pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
             pw.println();
 
-            pw.print("  Screen On Files:\n");
-            dumpMap(pw, "    ", mScreenOnFiles);
+            pw.print("  Interactive File values:\n");
+            dumpMap(pw, "    ", mFilesForInteractive);
             pw.println();
 
-            pw.print("  Screen Off Files:\n");
-            dumpMap(pw, "    ", mScreenOffFiles);
+            pw.print("  Noninteractive File values:\n");
+            dumpMap(pw, "    ", mFilesForNoninteractive);
             pw.println();
         }
     }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a47b809..584761c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3105,7 +3105,7 @@
         mIsVrModeEnabled = enabled;
     }
 
-    public static void powerHintInternal(int hintId, int data) {
+    private static void powerHintInternal(int hintId, int data) {
         nativeSendPowerHint(hintId, data);
     }
 
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index b3e8538..3db6a25 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -25,6 +25,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.PowerManagerInternal;
 import android.os.PowerManagerInternal.LowPowerModeListener;
 import android.os.PowerSaveState;
 import android.os.UserHandle;
@@ -33,7 +34,9 @@
 import android.widget.Toast;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
 import com.android.server.power.BatterySaverPolicy;
 import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener;
 import com.android.server.power.PowerManagerService;
@@ -63,20 +66,17 @@
     @GuardedBy("mLock")
     private boolean mEnabled;
 
-    /**
-     * Keep track of the previous enabled state, which we use to decide when to send broadcasts,
-     * which we don't want to send only when the screen state changes.
-     */
-    @GuardedBy("mLock")
-    private boolean mWasEnabled;
-
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             switch (intent.getAction()) {
                 case Intent.ACTION_SCREEN_ON:
                 case Intent.ACTION_SCREEN_OFF:
-                    mHandler.postStateChanged();
+                    if (!isEnabled()) {
+                        return; // No need to send it if not enabled.
+                    }
+                    // Don't send the broadcast, because we never did so in this case.
+                    mHandler.postStateChanged(/*sendBroadcast=*/ false);
                     break;
             }
         }
@@ -121,25 +121,32 @@
 
     @Override
     public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) {
-        mHandler.postStateChanged();
+        if (!isEnabled()) {
+            return; // No need to send it if not enabled.
+        }
+        mHandler.postStateChanged(/*sendBroadcast=*/ true);
     }
 
     private class MyHandler extends Handler {
-        private final int MSG_STATE_CHANGED = 1;
+        private static final int MSG_STATE_CHANGED = 1;
+
+        private static final int ARG_DONT_SEND_BROADCAST = 0;
+        private static final int ARG_SEND_BROADCAST = 1;
 
         public MyHandler(Looper looper) {
             super(looper);
         }
 
-        public void postStateChanged() {
-            obtainMessage(MSG_STATE_CHANGED).sendToTarget();
+        public void postStateChanged(boolean sendBroadcast) {
+            obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
+                    ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget();
         }
 
         @Override
         public void dispatchMessage(Message msg) {
             switch (msg.what) {
                 case MSG_STATE_CHANGED:
-                    handleBatterySaverStateChanged();
+                    handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST);
                     break;
             }
         }
@@ -155,38 +162,53 @@
             }
             mEnabled = enable;
 
-            mHandler.postStateChanged();
+            mHandler.postStateChanged(/*sendBroadcast=*/ true);
+        }
+    }
+
+    /** @return whether battery saver is enabled or not. */
+    boolean isEnabled() {
+        synchronized (mLock) {
+            return mEnabled;
         }
     }
 
     /**
      * Dispatch power save events to the listeners.
      *
-     * This is always called on the handler thread.
+     * This method is always called on the handler thread.
+     *
+     * This method is called only in the following cases:
+     * - When battery saver becomes activated.
+     * - When battery saver becomes deactivated.
+     * - When battery saver is on the interactive state changes.
+     * - When battery saver is on the battery saver policy changes.
      */
-    void handleBatterySaverStateChanged() {
+    void handleBatterySaverStateChanged(boolean sendBroadcast) {
         final LowPowerModeListener[] listeners;
 
-        final boolean wasEnabled;
         final boolean enabled;
-        final boolean isScreenOn = getPowerManager().isInteractive();
+        final boolean isInteractive = getPowerManager().isInteractive();
         final ArrayMap<String, String> fileValues;
 
         synchronized (mLock) {
-            Slog.i(TAG, "Battery saver enabled: screen on=" + isScreenOn);
+            Slog.i(TAG, "Battery saver " + (mEnabled ? "enabled" : "disabled")
+                    + ": isInteractive=" + isInteractive);
 
             listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
-            wasEnabled = mWasEnabled;
             enabled = mEnabled;
 
             if (enabled) {
-                fileValues = mBatterySaverPolicy.getFileValues(isScreenOn);
+                fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
             } else {
                 fileValues = null;
             }
         }
 
-        PowerManagerService.powerHintInternal(PowerHint.LOW_POWER, enabled ? 1 : 0);
+        final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+        if (pmi != null) {
+            pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);
+        }
 
         if (enabled) {
             // STOPSHIP Remove the toast.
@@ -195,13 +217,13 @@
                     Toast.LENGTH_LONG).show();
         }
 
-        if (fileValues == null || fileValues.size() == 0) {
+        if (ArrayUtils.isEmpty(fileValues)) {
             mFileUpdater.restoreDefault();
         } else {
             mFileUpdater.writeFiles(fileValues);
         }
 
-        if (enabled != wasEnabled) {
+        if (sendBroadcast) {
             if (DEBUG) {
                 Slog.i(TAG, "Sending broadcasts for mode: " + enabled);
             }
@@ -231,9 +253,5 @@
                 listener.onLowPowerModeChanged(result);
             }
         }
-
-        synchronized (mLock) {
-            mWasEnabled = enabled;
-        }
     }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index ef82de6..c145e82 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -144,7 +144,9 @@
 
     // Use a Testable subclass so we can simulate calls from the system without failing.
     private static class TestableNotificationManagerService extends NotificationManagerService {
-        public TestableNotificationManagerService(Context context) { super(context); }
+        public TestableNotificationManagerService(Context context) {
+            super(context);
+        }
 
         @Override
         protected boolean isCallingUidSystem() {
@@ -160,6 +162,11 @@
         protected ICompanionDeviceManager getCompanionManager() {
             return null;
         }
+
+        @Override
+        protected void reportSeen(NotificationRecord r) {
+            return;
+        }
     }
 
     @Before
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 4db9a30..36d0c8b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -17,12 +17,15 @@
 package com.android.server.pm.dex;
 
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.os.Build;
 import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.server.pm.Installer;
+
 import dalvik.system.DelegateLastClassLoader;
 import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
@@ -36,8 +39,13 @@
 import java.util.Map;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -45,6 +53,12 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
@@ -56,6 +70,12 @@
     private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
             DelegateLastClassLoader.class.getName();
 
+    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+    @Mock Installer mInstaller;
+    @Mock IPackageManager mPM;
+    private final Object mInstallLock = new Object();
+    @Mock DexManager.Listener mListener;
+
     private DexManager mDexManager;
 
     private TestData mFooUser0;
@@ -90,7 +110,8 @@
         mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
                 DELEGATE_LAST_CLASS_LOADER_NAME);
 
-        mDexManager = new DexManager(null, null, null, null);
+        mDexManager = new DexManager(
+            mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener);
 
         // Foo and Bar are available to user0.
         // Only Bar is available to user1;
@@ -440,6 +461,20 @@
 
     }
 
+    @Test
+    public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
+        List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
+        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+        when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
+                .thenReturn(mFooUser0.mPackageInfo);
+
+        mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
+
+        verify(mListener, times(fooSecondaries.size()))
+                .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
+                        any(DexUseInfo.class), anyString(), anyInt());
+    }
 
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
@@ -492,12 +527,12 @@
     }
 
     private PackageUseInfo getPackageUseInfo(TestData testData) {
-        assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
-        return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+        assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
+        return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
     }
 
     private void assertNoUseInfo(TestData testData) {
-        assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+        assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
     }
 
     private static PackageInfo getMockPackageInfo(String packageName, int userId) {
@@ -555,8 +590,8 @@
 
         List<String> getSecondaryDexPathsFromProtectedDirs() {
             List<String> paths = new ArrayList<>();
-            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
-            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
+            paths.add(mPackageInfo.applicationInfo.deviceProtectedDataDir + "/secondary6.dex");
+            paths.add(mPackageInfo.applicationInfo.credentialProtectedDataDir + "/secondary7.dex");
             return paths;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 5b6225e..0db19e4 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -18,13 +18,13 @@
 import android.os.PowerManager.ServiceType;
 import android.os.PowerSaveState;
 import android.os.Handler;
-import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArrayMap;
 
 import com.android.frameworks.servicestests.R;
+import com.android.internal.annotations.VisibleForTesting;
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -68,6 +68,12 @@
         int getDeviceSpecificConfigResId() {
             return mDeviceSpecificConfigResId;
         }
+
+
+        @VisibleForTesting
+        void onChange() {
+            onChange(true, null);
+        }
     }
 
     @Mock
@@ -221,14 +227,14 @@
         mMockGlobalSettings.put(Global.BATTERY_SAVER_CONSTANTS, "");
         mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, "");
 
-        mBatterySaverPolicy.onChangeForTest();
+        mBatterySaverPolicy.onChange();
         assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
         assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
 
 
         mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_2;
 
-        mBatterySaverPolicy.onChangeForTest();
+        mBatterySaverPolicy.onChange();
         assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
         assertThat(mBatterySaverPolicy.getFileValues(false).toString())
                 .isEqualTo("{/sys/a=1, /sys/b=2}");
@@ -236,7 +242,7 @@
 
         mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_3;
 
-        mBatterySaverPolicy.onChangeForTest();
+        mBatterySaverPolicy.onChange();
         assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{/proc/c=4}");
         assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{/sys/a=3}");
 
@@ -244,7 +250,7 @@
         mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
                 "file-on:/proc/z=4");
 
-        mBatterySaverPolicy.onChangeForTest();
+        mBatterySaverPolicy.onChange();
         assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{/proc/z=4}");
         assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
     }
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 8531baf..40edfd2 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -17,6 +17,9 @@
 package com.android.server.usage;
 
 import static android.app.usage.AppStandby.*;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -67,6 +70,10 @@
     private static final long HOUR_MS = 60 * MINUTE_MS;
     private static final long DAY_MS = 24 * HOUR_MS;
 
+    private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
+    private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
+    private static final long RARE_THRESHOLD = 48 * HOUR_MS;
+
     private MyInjector mInjector;
 
     static class MyContextWrapper extends ContextWrapper {
@@ -168,6 +175,14 @@
             return packageName != null && packageName.equals(mBoundWidgetPackage);
         }
 
+        @Override
+        String getAppIdleSettings() {
+            return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
+                    + WORKING_SET_THRESHOLD + "/"
+                    + FREQUENT_THRESHOLD + "/"
+                    + RARE_THRESHOLD;
+        }
+
         // Internal methods
 
         void setDisplayOn(boolean on) {
@@ -225,12 +240,12 @@
         AppStandbyController controller = setupController();
 
         setChargingState(controller, true);
-        mInjector.mElapsedRealtime = 8 * DAY_MS;
+        mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
         assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
                 mInjector.mElapsedRealtime, false));
 
         setChargingState(controller, false);
-        mInjector.mElapsedRealtime = 16 * DAY_MS;
+        mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2;
         controller.checkIdleStates(USER_ID);
         assertTrue(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
                 mInjector.mElapsedRealtime, false));
@@ -247,43 +262,49 @@
                         false));
     }
 
-    private void reportEvent(AppStandbyController controller, long elapsedTime) {
+    private void reportEvent(AppStandbyController controller, int eventType,
+            long elapsedTime) {
         // Back to ACTIVE on event
         UsageEvents.Event ev = new UsageEvents.Event();
         ev.mPackage = PACKAGE_1;
-        ev.mEventType = UsageEvents.Event.USER_INTERACTION;
+        ev.mEventType = eventType;
         controller.reportEvent(ev, elapsedTime, USER_ID);
     }
 
+    private int getStandbyBucket(AppStandbyController controller) {
+        return controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
+                true);
+    }
+
     @Test
     public void testBuckets() throws Exception {
         AppStandbyController controller = setupController();
 
         assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
 
-        reportEvent(controller, 0);
+        reportEvent(controller, USER_INTERACTION, 0);
 
         // ACTIVE bucket
-        assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
 
         // WORKING_SET bucket
-        assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
 
         // WORKING_SET bucket
-        assertTimeout(controller, 47 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(controller, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
 
         // FREQUENT bucket
-        assertTimeout(controller, 4 * DAY_MS, STANDBY_BUCKET_FREQUENT);
+        assertTimeout(controller, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
 
         // RARE bucket
-        assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);
+        assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
 
-        reportEvent(controller, 9 * DAY_MS);
+        reportEvent(controller, USER_INTERACTION, RARE_THRESHOLD + 1);
 
-        assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
 
         // RARE bucket
-        assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+        assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
     }
 
     @Test
@@ -293,23 +314,21 @@
 
         assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
 
-        reportEvent(controller, 0);
+        reportEvent(controller, USER_INTERACTION, 0);
 
         // ACTIVE bucket
-        assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
 
         // WORKING_SET bucket
-        assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
 
         // RARE bucket, should fail because the screen wasn't ON.
-        mInjector.mElapsedRealtime = 9 * DAY_MS;
+        mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
         controller.checkIdleStates(USER_ID);
-        assertNotEquals(STANDBY_BUCKET_RARE,
-                controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
-                false));
+        assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
 
         mInjector.setDisplayOn(true);
-        assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+        assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
     }
 
     @Test
@@ -318,8 +337,7 @@
         setChargingState(controller, false);
 
         controller.forceIdleState(PACKAGE_1, USER_ID, true);
-        assertEquals(STANDBY_BUCKET_RARE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
-                true));
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
         assertTrue(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
 
         controller.forceIdleState(PACKAGE_1, USER_ID, false);
@@ -327,4 +345,20 @@
                 true));
         assertFalse(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
     }
+
+    @Test
+    public void testNotificationEvent() throws Exception {
+        AppStandbyController controller = setupController();
+        setChargingState(controller, false);
+
+        reportEvent(controller, USER_INTERACTION, 0);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+        mInjector.mElapsedRealtime = 1;
+        reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+
+        controller.forceIdleState(PACKAGE_1, USER_ID, true);
+        reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index c5ca330..1e5eb05 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -186,7 +186,7 @@
         writeScreenOnTime();
     }
 
-    public void reportUsage(String packageName, int userId, long elapsedRealtime) {
+    public int reportUsage(String packageName, int userId, long elapsedRealtime) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                 elapsedRealtime, true);
@@ -197,12 +197,33 @@
                 + (elapsedRealtime - mElapsedSnapshot);
         appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
         appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
-        appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_ACTIVE;
-        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
-        if (DEBUG) {
-            Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
-                    + ", reason=" + appUsageHistory.bucketingReason);
+        if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
+            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+            if (DEBUG) {
+                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+                        + ", reason=" + appUsageHistory.bucketingReason);
+            }
         }
+        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+
+        return appUsageHistory.currentBucket;
+    }
+
+    public int reportMildUsage(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+                elapsedRealtime, true);
+        if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
+            appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
+            if (DEBUG) {
+                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+                        + ", reason=" + appUsageHistory.bucketingReason);
+            }
+        }
+        // TODO: Should this be a different reason for partial usage?
+        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+
+        return appUsageHistory.currentBucket;
     }
 
     public void setIdle(String packageName, int userId, long elapsedRealtime) {
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 5623a68..98a2e28 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -91,14 +91,14 @@
             0,
             0,
             COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
-            COMPRESS_TIME ? 240 * 1000 : 8 * ONE_HOUR
+            COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR
     };
 
     static final long[] ELAPSED_TIME_THRESHOLDS = {
             0,
             COMPRESS_TIME ?  1 * ONE_MINUTE : 12 * ONE_HOUR,
-            COMPRESS_TIME ?  4 * ONE_MINUTE :  2 * ONE_DAY,
-            COMPRESS_TIME ? 16 * ONE_MINUTE :  8 * ONE_DAY
+            COMPRESS_TIME ?  4 * ONE_MINUTE : 24 * ONE_HOUR,
+            COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR
     };
 
     static final int[] THRESHOLD_BUCKETS = {
@@ -229,6 +229,7 @@
         // Get sync adapters for the authority
         String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
                 authority, userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
         for (String packageName: packages) {
             // Only force the sync adapters to active if the provider is not in the same package and
             // the sync adapter is a system package.
@@ -239,7 +240,12 @@
                     continue;
                 }
                 if (!packageName.equals(providerPkgName)) {
-                    setAppIdleAsync(packageName, false, userId);
+                    synchronized (mAppIdleLock) {
+                        int newBucket = mAppIdleHistory.reportMildUsage(packageName, userId,
+                                    elapsedRealtime);
+                        maybeInformListeners(packageName, userId, elapsedRealtime,
+                                newBucket);
+                    }
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 // Shouldn't happen
@@ -493,11 +499,21 @@
             if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
                     || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
                     || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
-                    || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
-                mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+                    || event.mEventType == UsageEvents.Event.USER_INTERACTION
+                    || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN)) {
+
+                final int newBucket;
+                if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
+                    newBucket = mAppIdleHistory.reportMildUsage(event.mPackage, userId,
+                            elapsedRealtime);
+                } else {
+                    newBucket = mAppIdleHistory.reportUsage(event.mPackage, userId,
+                            elapsedRealtime);
+                }
+
+                maybeInformListeners(event.mPackage, userId, elapsedRealtime,
+                        newBucket);
                 if (previouslyIdle) {
-                    maybeInformListeners(event.mPackage, userId, elapsedRealtime,
-                            AppStandby.STANDBY_BUCKET_ACTIVE);
                     notifyBatteryStats(event.mPackage, userId, false);
                 }
             }
@@ -1033,6 +1049,11 @@
                 int userId) {
             return appWidgetManager.isBoundWidgetPackage(packageName, userId);
         }
+
+        String getAppIdleSettings() {
+            return Settings.Global.getString(mContext.getContentResolver(),
+                    Settings.Global.APP_IDLE_CONSTANTS);
+        }
     }
 
     class AppStandbyHandler extends Handler {
@@ -1165,8 +1186,7 @@
                 // Look at global settings for this.
                 // TODO: Maybe apply different thresholds for different users.
                 try {
-                    mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
-                            Settings.Global.APP_IDLE_CONSTANTS));
+                    mParser.setString(mInjector.getAppIdleSettings());
                 } catch (IllegalArgumentException e) {
                     Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
                     // fallthrough, mParser is empty and all defaults will be returned.
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 0b10590..f02221c 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -643,6 +643,8 @@
                 return "SHORTCUT_INVOCATION";
             case UsageEvents.Event.CHOOSER_ACTION:
                 return "CHOOSER_ACTION";
+            case UsageEvents.Event.NOTIFICATION_SEEN:
+                return "NOTIFICATION_SEEN";
             default:
                 return "UNKNOWN";
         }
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index f30d7bd..05480dc 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -1065,7 +1065,7 @@
      *
      * @param state The audio state of this {@code RemoteConnection}.
      * @hide
-     * @deprecated Use {@link #setCallAudioState(CallAudioState) instead.
+     * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
      */
     @SystemApi
     @Deprecated
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 6276626..c968406 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -101,9 +101,6 @@
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
      *
-     * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
-     * not a 2 or 3-digit code.
-     *
      * @hide
      */
     public CellIdentityGsm (int lac, int cid, int arfcn, int bsic, String mccStr,
@@ -115,22 +112,29 @@
         // for inbound parcels
         mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
 
+        // Only allow INT_MAX if unknown string mcc/mnc
         if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
             mMccStr = mccStr;
-        } else if (mccStr.isEmpty()) {
-            // If the mccStr parsed from Parcel is empty, set it as null.
+        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mccStr is empty or unknown, set it as null.
             mMccStr = null;
         } else {
-            throw new IllegalArgumentException("invalid MCC format");
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+            // after the bug got fixed.
+            mMccStr = null;
+            log("invalid MCC format: " + mccStr);
         }
 
         if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
             mMncStr = mncStr;
-        } else if (mncStr.isEmpty()) {
-            // If the mncStr parsed from Parcel is empty, set it as null.
+        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mncStr is empty or unknown, set it as null.
             mMncStr = null;
         } else {
-            throw new IllegalArgumentException("invalid MNC format");
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+            // after the bug got fixed.
+            mMncStr = null;
+            log("invalid MNC format: " + mncStr);
         }
 
         mAlphaLong = alphal;
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 74d2966..825dcc3 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -102,9 +102,6 @@
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
      *
-     * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
-     * not a 2 or 3-digit code.
-     *
      * @hide
      */
     public CellIdentityLte (int ci, int pci, int tac, int earfcn, String mccStr,
@@ -114,22 +111,29 @@
         mTac = tac;
         mEarfcn = earfcn;
 
+        // Only allow INT_MAX if unknown string mcc/mnc
         if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
             mMccStr = mccStr;
-        } else if (mccStr.isEmpty()) {
-            // If the mccStr parsed from Parcel is empty, set it as null.
+        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mccStr is empty or unknown, set it as null.
             mMccStr = null;
         } else {
-            throw new IllegalArgumentException("invalid MCC format");
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+            // after the bug got fixed.
+            mMccStr = null;
+            log("invalid MCC format: " + mccStr);
         }
 
         if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
             mMncStr = mncStr;
-        } else if (mncStr.isEmpty()) {
-            // If the mncStr parsed from Parcel is empty, set it as null.
+        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mncStr is empty or unknown, set it as null.
             mMncStr = null;
         } else {
-            throw new IllegalArgumentException("invalid MNC format");
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+            // after the bug got fixed.
+            mMncStr = null;
+            log("invalid MNC format: " + mncStr);
         }
 
         mAlphaLong = alphal;
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 51b11aa..e74b570 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -102,9 +102,6 @@
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
      *
-     * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
-     * not a 2 or 3-digit code.
-     *
      * @hide
      */
     public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
@@ -114,22 +111,29 @@
         mPsc = psc;
         mUarfcn = uarfcn;
 
+        // Only allow INT_MAX if unknown string mcc/mnc
         if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
             mMccStr = mccStr;
-        } else if (mccStr.isEmpty()) {
-            // If the mccStr parsed from Parcel is empty, set it as null.
+        } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mccStr is empty or unknown, set it as null.
             mMccStr = null;
         } else {
-            throw new IllegalArgumentException("invalid MCC format");
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+            // after the bug got fixed.
+            mMccStr = null;
+            log("invalid MCC format: " + mccStr);
         }
 
         if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
             mMncStr = mncStr;
-        } else if (mncStr.isEmpty()) {
-            // If the mncStr parsed from Parcel is empty, set it as null.
+        } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mncStr is empty or unknown, set it as null.
             mMncStr = null;
         } else {
-            throw new IllegalArgumentException("invalid MNC format");
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+            // after the bug got fixed.
+            mMncStr = null;
+            log("invalid MNC format: " + mncStr);
         }
 
         mAlphaLong = alphal;
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 08efc27..0f6fb50 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -218,6 +218,7 @@
       if (attr.id) {
         printer_->Print(attr.id.value().to_string());
       }
+      printer_->Println();
     }
   }
 
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 96a0203..6fcf0f6 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -268,6 +268,11 @@
   return out << res_id.to_string();
 }
 
+// For generic code to call 'using std::to_string; to_string(T);'.
+inline std::string to_string(const ResourceId& id) {
+  return id.to_string();
+}
+
 //
 // ResourceType implementation.
 //
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 66b5a7a..e94c0b4 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -16,7 +16,6 @@
 
 #include <sys/stat.h>
 
-#include <fstream>
 #include <queue>
 #include <unordered_map>
 #include <vector>
@@ -212,6 +211,8 @@
 // This delegate will attempt to masquerade any '@id/' references with ID 0xPPTTEEEE,
 // where PP > 7f, as 0x7fPPEEEE. Any potential overlapping is verified and an error occurs if such
 // an overlap exists.
+//
+// See b/37498913.
 class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
  public:
   FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
@@ -652,24 +653,26 @@
 static bool WriteStableIdMapToPath(IDiagnostics* diag,
                                    const std::unordered_map<ResourceName, ResourceId>& id_map,
                                    const std::string& id_map_path) {
-  std::ofstream fout(id_map_path, std::ofstream::binary);
-  if (!fout) {
-    diag->Error(DiagMessage(id_map_path) << strerror(errno));
+  io::FileOutputStream fout(id_map_path);
+  if (fout.HadError()) {
+    diag->Error(DiagMessage(id_map_path) << "failed to open: " << fout.GetError());
     return false;
   }
 
+  text::Printer printer(&fout);
   for (const auto& entry : id_map) {
     const ResourceName& name = entry.first;
     const ResourceId& id = entry.second;
-    fout << name << " = " << id << "\n";
+    printer.Print(name.to_string());
+    printer.Print(" = ");
+    printer.Println(id.to_string());
   }
+  fout.Flush();
 
-  if (!fout) {
-    diag->Error(DiagMessage(id_map_path) << "failed writing to file: "
-                                         << android::base::SystemErrorCodeToString(errno));
+  if (fout.HadError()) {
+    diag->Error(DiagMessage(id_map_path) << "failed writing to file: " << fout.GetError());
     return false;
   }
-
   return true;
 }
 
@@ -985,36 +988,35 @@
 
     file::AppendPath(&out_path, "R.java");
 
-    std::ofstream fout(out_path, std::ofstream::binary);
-    if (!fout) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "failed writing to '" << out_path
-                                        << "': " << android::base::SystemErrorCodeToString(errno));
+    io::FileOutputStream fout(out_path);
+    if (fout.HadError()) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+                                                      << "': " << fout.GetError());
       return false;
     }
 
-    std::unique_ptr<std::ofstream> fout_text;
+    std::unique_ptr<io::FileOutputStream> fout_text;
     if (out_text_symbols_path) {
-      fout_text =
-          util::make_unique<std::ofstream>(out_text_symbols_path.value(), std::ofstream::binary);
-      if (!*fout_text) {
-        context_->GetDiagnostics()->Error(
-            DiagMessage() << "failed writing to '" << out_text_symbols_path.value()
-                          << "': " << android::base::SystemErrorCodeToString(errno));
+      fout_text = util::make_unique<io::FileOutputStream>(out_text_symbols_path.value());
+      if (fout_text->HadError()) {
+        context_->GetDiagnostics()->Error(DiagMessage()
+                                          << "failed writing to '" << out_text_symbols_path.value()
+                                          << "': " << fout_text->GetError());
         return false;
       }
     }
 
     JavaClassGenerator generator(context_, table, java_options);
     if (!generator.Generate(package_name_to_generate, out_package, &fout, fout_text.get())) {
-      context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.getError());
+      context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.GetError());
       return false;
     }
 
-    if (!fout) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "failed writing to '" << out_path
-                                        << "': " << android::base::SystemErrorCodeToString(errno));
+    fout.Flush();
+
+    if (fout.HadError()) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+                                                      << "': " << fout.GetError());
       return false;
     }
     return true;
@@ -1142,18 +1144,19 @@
 
     file::AppendPath(&out_path, "Manifest.java");
 
-    std::ofstream fout(out_path, std::ofstream::binary);
-    if (!fout) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "failed writing to '" << out_path
-                                        << "': " << android::base::SystemErrorCodeToString(errno));
+    io::FileOutputStream fout(out_path);
+    if (fout.HadError()) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
+                                                      << "': " << fout.GetError());
       return false;
     }
 
-    if (!ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout)) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "failed writing to '" << out_path
-                                        << "': " << android::base::SystemErrorCodeToString(errno));
+    ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout);
+    fout.Flush();
+
+    if (fout.HadError()) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+                                                      << "': " << fout.GetError());
       return false;
     }
     return true;
@@ -1165,19 +1168,19 @@
     }
 
     const std::string& out_path = out.value();
-    std::ofstream fout(out_path, std::ofstream::binary);
-    if (!fout) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "failed to open '" << out_path
-                                        << "': " << android::base::SystemErrorCodeToString(errno));
+    io::FileOutputStream fout(out_path);
+    if (fout.HadError()) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
+                                                      << "': " << fout.GetError());
       return false;
     }
 
-    proguard::WriteKeepSet(&fout, keep_set);
-    if (!fout) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "failed writing to '" << out_path
-                                        << "': " << android::base::SystemErrorCodeToString(errno));
+    proguard::WriteKeepSet(keep_set, &fout);
+    fout.Flush();
+
+    if (fout.HadError()) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+                                                      << "': " << fout.GetError());
       return false;
     }
     return true;
diff --git a/tools/aapt2/io/FileStream.cpp b/tools/aapt2/io/FileStream.cpp
index 4ff6d78..27529bc 100644
--- a/tools/aapt2/io/FileStream.cpp
+++ b/tools/aapt2/io/FileStream.cpp
@@ -25,6 +25,12 @@
 #include "android-base/macros.h"
 #include "android-base/utf8.h"
 
+#if defined(_WIN32)
+// This is only needed for O_CLOEXEC.
+#include <windows.h>
+#define O_CLOEXEC O_NOINHERIT
+#endif
+
 using ::android::base::SystemErrorCodeToString;
 using ::android::base::unique_fd;
 
@@ -32,18 +38,20 @@
 namespace io {
 
 FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity)
-    : FileInputStream(::android::base::utf8::open(path.c_str(), O_RDONLY | O_BINARY),
-                      buffer_capacity) {
+    : buffer_capacity_(buffer_capacity) {
+  int mode = O_RDONLY | O_CLOEXEC | O_BINARY;
+  fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode)));
+  if (fd_ == -1) {
+    error_ = SystemErrorCodeToString(errno);
+  } else {
+    buffer_.reset(new uint8_t[buffer_capacity_]);
+  }
 }
 
 FileInputStream::FileInputStream(int fd, size_t buffer_capacity)
-    : fd_(fd),
-      buffer_capacity_(buffer_capacity),
-      buffer_offset_(0u),
-      buffer_size_(0u),
-      total_byte_count_(0u) {
-  if (fd_ == -1) {
-    error_ = SystemErrorCodeToString(errno);
+    : fd_(fd), buffer_capacity_(buffer_capacity) {
+  if (fd_ < 0) {
+    error_ = "Bad File Descriptor";
   } else {
     buffer_.reset(new uint8_t[buffer_capacity_]);
   }
@@ -100,9 +108,16 @@
   return error_;
 }
 
-FileOutputStream::FileOutputStream(const std::string& path, int mode, size_t buffer_capacity)
-    : FileOutputStream(unique_fd(::android::base::utf8::open(path.c_str(), mode)),
-                       buffer_capacity) {
+FileOutputStream::FileOutputStream(const std::string& path, size_t buffer_capacity)
+    : buffer_capacity_(buffer_capacity) {
+  int mode = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;
+  owned_fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode, 0666)));
+  fd_ = owned_fd_.get();
+  if (fd_ < 0) {
+    error_ = SystemErrorCodeToString(errno);
+  } else {
+    buffer_.reset(new uint8_t[buffer_capacity_]);
+  }
 }
 
 FileOutputStream::FileOutputStream(unique_fd fd, size_t buffer_capacity)
@@ -111,9 +126,9 @@
 }
 
 FileOutputStream::FileOutputStream(int fd, size_t buffer_capacity)
-    : fd_(fd), buffer_capacity_(buffer_capacity), buffer_offset_(0u), total_byte_count_(0u) {
-  if (fd_ == -1) {
-    error_ = SystemErrorCodeToString(errno);
+    : fd_(fd), buffer_capacity_(buffer_capacity) {
+  if (fd_ < 0) {
+    error_ = "Bad File Descriptor";
   } else {
     buffer_.reset(new uint8_t[buffer_capacity_]);
   }
diff --git a/tools/aapt2/io/FileStream.h b/tools/aapt2/io/FileStream.h
index 4ed1ad5..62d910f 100644
--- a/tools/aapt2/io/FileStream.h
+++ b/tools/aapt2/io/FileStream.h
@@ -22,7 +22,6 @@
 #include <memory>
 #include <string>
 
-#include "android-base/file.h"  // for O_BINARY
 #include "android-base/macros.h"
 #include "android-base/unique_fd.h"
 
@@ -55,15 +54,15 @@
   android::base::unique_fd fd_;
   std::string error_;
   std::unique_ptr<uint8_t[]> buffer_;
-  size_t buffer_capacity_;
-  size_t buffer_offset_;
-  size_t buffer_size_;
-  size_t total_byte_count_;
+  size_t buffer_capacity_ = 0u;
+  size_t buffer_offset_ = 0u;
+  size_t buffer_size_ = 0u;
+  size_t total_byte_count_ = 0u;
 };
 
 class FileOutputStream : public OutputStream {
  public:
-  explicit FileOutputStream(const std::string& path, int mode = O_RDWR | O_CREAT | O_BINARY,
+  explicit FileOutputStream(const std::string& path,
                             size_t buffer_capacity = kDefaultBufferCapacity);
 
   // Does not take ownership of `fd`.
@@ -97,9 +96,9 @@
   int fd_;
   std::string error_;
   std::unique_ptr<uint8_t[]> buffer_;
-  size_t buffer_capacity_;
-  size_t buffer_offset_;
-  size_t total_byte_count_;
+  size_t buffer_capacity_ = 0u;
+  size_t buffer_offset_ = 0u;
+  size_t total_byte_count_ = 0u;
 };
 
 }  // namespace io
diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index a6d58ca..c0eaa8e 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -16,6 +16,7 @@
 
 #include "io/FileStream.h"
 
+#include "android-base/file.h"
 #include "android-base/macros.h"
 #include "android-base/test_utils.h"
 
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index c93461a..8d91b00 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -23,6 +23,7 @@
 #include "text/Utf8Iterator.h"
 #include "util/Util.h"
 
+using ::aapt::text::Printer;
 using ::aapt::text::Utf8Iterator;
 using ::android::StringPiece;
 
@@ -109,23 +110,22 @@
   }
 }
 
-void AnnotationProcessor::WriteToStream(const StringPiece& prefix, std::ostream* out) const {
+void AnnotationProcessor::Print(Printer* printer) const {
   if (has_comments_) {
     std::string result = comment_.str();
     for (StringPiece line : util::Tokenize(result, '\n')) {
-      *out << prefix << line << "\n";
+      printer->Println(line);
     }
-    *out << prefix << " */"
-         << "\n";
+    printer->Println(" */");
   }
 
   if (annotation_bit_mask_ & AnnotationRule::kDeprecated) {
-    *out << prefix << "@Deprecated\n";
+    printer->Println("@Deprecated");
   }
 
   for (const AnnotationRule& rule : sAnnotationRules) {
     if (annotation_bit_mask_ & rule.bit_mask) {
-      *out << prefix << rule.annotation << "\n";
+      printer->Println(rule.annotation);
     }
   }
 }
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index a7bf73f..ae7bdb0 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -22,6 +22,8 @@
 
 #include "androidfw/StringPiece.h"
 
+#include "text/Printer.h"
+
 namespace aapt {
 
 // Builds a JavaDoc comment from a set of XML comments.
@@ -61,8 +63,8 @@
 
   void AppendNewLine();
 
-  // Writes the comments and annotations to the stream, with the given prefix before each line.
-  void WriteToStream(const android::StringPiece& prefix, std::ostream* out) const;
+  // Writes the comments and annotations to the Printer.
+  void Print(text::Printer* printer) const;
 
  private:
   std::stringstream comment_;
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index 856f4cc..69f49c8 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -16,8 +16,12 @@
 
 #include "java/AnnotationProcessor.h"
 
+#include "io/StringStream.h"
 #include "test/Test.h"
+#include "text/Printer.h"
 
+using ::aapt::io::StringOutputStream;
+using ::aapt::text::Printer;
 using ::testing::Eq;
 using ::testing::HasSubstr;
 using ::testing::Not;
@@ -33,9 +37,11 @@
   AnnotationProcessor processor;
   processor.AppendComment(comment);
 
-  std::stringstream result;
-  processor.WriteToStream("", &result);
-  std::string annotations = result.str();
+  std::string annotations;
+  StringOutputStream out(&annotations);
+  Printer printer(&out);
+  processor.Print(&printer);
+  out.Flush();
 
   EXPECT_THAT(annotations, HasSubstr("@Deprecated"));
 }
@@ -44,9 +50,11 @@
   AnnotationProcessor processor;
   processor.AppendComment("@SystemApi This is a system API");
 
-  std::stringstream result;
-  processor.WriteToStream("", &result);
-  std::string annotations = result.str();
+  std::string annotations;
+  StringOutputStream out(&annotations);
+  Printer printer(&out);
+  processor.Print(&printer);
+  out.Flush();
 
   EXPECT_THAT(annotations, HasSubstr("@android.annotation.SystemApi"));
   EXPECT_THAT(annotations, Not(HasSubstr("@SystemApi")));
@@ -57,9 +65,11 @@
   AnnotationProcessor processor;
   processor.AppendComment("@TestApi This is a test API");
 
-  std::stringstream result;
-  processor.WriteToStream("", &result);
-  std::string annotations = result.str();
+  std::string annotations;
+  StringOutputStream out(&annotations);
+  Printer printer(&out);
+  processor.Print(&printer);
+  out.Flush();
 
   EXPECT_THAT(annotations, HasSubstr("@android.annotation.TestApi"));
   EXPECT_THAT(annotations, Not(HasSubstr("@TestApi")));
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index 0c57e7e..b692ccf 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -18,25 +18,27 @@
 
 #include "androidfw/StringPiece.h"
 
+using ::aapt::text::Printer;
 using ::android::StringPiece;
 
 namespace aapt {
 
-void ClassMember::WriteToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
-  processor_.WriteToStream(prefix, out);
+void ClassMember::Print(bool /*final*/, Printer* printer) const {
+  processor_.Print(printer);
 }
 
 void MethodDefinition::AppendStatement(const StringPiece& statement) {
   statements_.push_back(statement.to_string());
 }
 
-void MethodDefinition::WriteToStream(const StringPiece& prefix, bool final,
-                                     std::ostream* out) const {
-  *out << prefix << signature_ << " {\n";
+void MethodDefinition::Print(bool final, Printer* printer) const {
+  printer->Print(signature_).Println(" {");
+  printer->Indent();
   for (const auto& statement : statements_) {
-    *out << prefix << "  " << statement << "\n";
+    printer->Println(statement);
   }
-  *out << prefix << "}";
+  printer->Undent();
+  printer->Print("}");
 }
 
 ClassDefinition::Result ClassDefinition::AddMember(std::unique_ptr<ClassMember> member) {
@@ -62,34 +64,32 @@
   return true;
 }
 
-void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final,
-                                    std::ostream* out) const {
+void ClassDefinition::Print(bool final, Printer* printer) const {
   if (empty() && !create_if_empty_) {
     return;
   }
 
-  ClassMember::WriteToStream(prefix, final, out);
+  ClassMember::Print(final, printer);
 
-  *out << prefix << "public ";
+  printer->Print("public ");
   if (qualifier_ == ClassQualifier::kStatic) {
-    *out << "static ";
+    printer->Print("static ");
   }
-  *out << "final class " << name_ << " {\n";
-
-  std::string new_prefix = prefix.to_string();
-  new_prefix.append(kIndent);
+  printer->Print("final class ").Print(name_).Println(" {");
+  printer->Indent();
 
   for (const std::unique_ptr<ClassMember>& member : ordered_members_) {
     // There can be nullptr members when a member is added to the ClassDefinition
     // and takes precedence over a previous member with the same name. The overridden member is
     // set to nullptr.
     if (member != nullptr) {
-      member->WriteToStream(new_prefix, final, out);
-      *out << "\n";
+      member->Print(final, printer);
+      printer->Println();
     }
   }
 
-  *out << prefix << "}";
+  printer->Undent();
+  printer->Print("}");
 }
 
 constexpr static const char* sWarningHeader =
@@ -100,11 +100,12 @@
     " * should not be modified by hand.\n"
     " */\n\n";
 
-bool ClassDefinition::WriteJavaFile(const ClassDefinition* def, const StringPiece& package,
-                                    bool final, std::ostream* out) {
-  *out << sWarningHeader << "package " << package << ";\n\n";
-  def->WriteToStream("", final, out);
-  return bool(*out);
+void ClassDefinition::WriteJavaFile(const ClassDefinition* def, const StringPiece& package,
+                                    bool final, io::OutputStream* out) {
+  Printer printer(out);
+  printer.Print(sWarningHeader).Print("package ").Print(package).Println(";");
+  printer.Println();
+  def->Print(final, &printer);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 28a3489..fb11266 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -17,7 +17,6 @@
 #ifndef AAPT_JAVA_CLASSDEFINITION_H
 #define AAPT_JAVA_CLASSDEFINITION_H
 
-#include <ostream>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -27,6 +26,7 @@
 
 #include "Resource.h"
 #include "java/AnnotationProcessor.h"
+#include "text/Printer.h"
 #include "util/Util.h"
 
 namespace aapt {
@@ -47,11 +47,10 @@
 
   virtual const std::string& GetName() const = 0;
 
-  // Writes the class member to the out stream. Subclasses should derive this method
+  // Writes the class member to the Printer. Subclasses should derive this method
   // to write their own data. Call this base method from the subclass to write out
   // this member's comments/annotations.
-  virtual void WriteToStream(const android::StringPiece& prefix, bool final,
-                             std::ostream* out) const;
+  virtual void Print(bool final, text::Printer* printer) const;
 
  private:
   AnnotationProcessor processor_;
@@ -71,11 +70,16 @@
     return name_;
   }
 
-  void WriteToStream(const android::StringPiece& prefix, bool final,
-                     std::ostream* out) const override {
-    ClassMember::WriteToStream(prefix, final, out);
-    *out << prefix << "public static " << (final ? "final " : "") << "int " << name_ << "=" << val_
-         << ";";
+  void Print(bool final, text::Printer* printer) const override {
+    using std::to_string;
+
+    ClassMember::Print(final, printer);
+
+    printer->Print("public static ");
+    if (final) {
+      printer->Print("final ");
+    }
+    printer->Print("int ").Print(name_).Print("=").Print(to_string(val_)).Print(";");
   }
 
  private:
@@ -100,12 +104,14 @@
     return name_;
   }
 
-  void WriteToStream(const android::StringPiece& prefix, bool final,
-                     std::ostream* out) const override {
-    ClassMember::WriteToStream(prefix, final, out);
+  void Print(bool final, text::Printer* printer) const override {
+    ClassMember::Print(final, printer);
 
-    *out << prefix << "public static " << (final ? "final " : "") << "String "
-         << name_ << "=\"" << val_ << "\";";
+    printer->Print("public static ");
+    if (final) {
+      printer->Print("final ");
+    }
+    printer->Print("String ").Print(name_).Print("=\"").Print(val_).Print("\";");
   }
 
  private:
@@ -136,25 +142,27 @@
     return name_;
   }
 
-  void WriteToStream(const android::StringPiece& prefix, bool final,
-                     std::ostream* out) const override {
-    ClassMember::WriteToStream(prefix, final, out);
+  void Print(bool final, text::Printer* printer) const override {
+    ClassMember::Print(final, printer);
 
-    *out << prefix << "public static final int[] " << name_ << "={";
+    printer->Print("public static final int[] ").Print(name_).Print("={");
+    printer->Indent();
 
     const auto begin = elements_.begin();
     const auto end = elements_.end();
     for (auto current = begin; current != end; ++current) {
       if (std::distance(begin, current) % kAttribsPerLine == 0) {
-        *out << "\n" << prefix << kIndent << kIndent;
+        printer->Println();
       }
 
-      *out << *current;
+      printer->Print(to_string(*current));
       if (std::distance(current, end) > 1) {
-        *out << ", ";
+        printer->Print(", ");
       }
     }
-    *out << "\n" << prefix << kIndent << "};";
+    printer->Println();
+    printer->Undent();
+    printer->Print("};");
   }
 
  private:
@@ -187,8 +195,7 @@
     return false;
   }
 
-  void WriteToStream(const android::StringPiece& prefix, bool final,
-                     std::ostream* out) const override;
+  void Print(bool final, text::Printer* printer) const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MethodDefinition);
@@ -201,8 +208,8 @@
 
 class ClassDefinition : public ClassMember {
  public:
-  static bool WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
-                            bool final, std::ostream* out);
+  static void WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
+                            bool final, io::OutputStream* out);
 
   ClassDefinition(const android::StringPiece& name, ClassQualifier qualifier, bool createIfEmpty)
       : name_(name.to_string()), qualifier_(qualifier), create_if_empty_(createIfEmpty) {}
@@ -220,8 +227,7 @@
     return name_;
   }
 
-  void WriteToStream(const android::StringPiece& prefix, bool final,
-                     std::ostream* out) const override;
+  void Print(bool final, text::Printer* printer) const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 91cef64..9861770 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -37,8 +37,10 @@
 #include "java/ClassDefinition.h"
 #include "process/SymbolTable.h"
 
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::aapt::io::OutputStream;
+using ::aapt::text::Printer;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
 
 namespace aapt {
 
@@ -230,7 +232,7 @@
                                           const StringPiece& package_name_to_generate,
                                           ClassDefinition* out_class_def,
                                           MethodDefinition* out_rewrite_method,
-                                          std::ostream* out_r_txt) {
+                                          Printer* r_txt_printer) {
   const std::string array_field_name = TransformToFieldName(name.entry);
   std::unique_ptr<ResourceArrayMember> array_def =
       util::make_unique<ResourceArrayMember>(array_field_name);
@@ -323,8 +325,8 @@
     array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
   }
 
-  if (out_r_txt != nullptr) {
-    *out_r_txt << "int[] styleable " << array_field_name << " {";
+  if (r_txt_printer != nullptr) {
+    r_txt_printer->Print("int[] styleable ").Print(array_field_name).Print(" {");
   }
 
   // Add the ResourceIds to the array member.
@@ -332,16 +334,16 @@
     const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
     array_def->AddElement(id);
 
-    if (out_r_txt != nullptr) {
+    if (r_txt_printer != nullptr) {
       if (i != 0) {
-        *out_r_txt << ",";
+        r_txt_printer->Print(",");
       }
-      *out_r_txt << " " << id;
+      r_txt_printer->Print(" ").Print(id.to_string());
     }
   }
 
-  if (out_r_txt != nullptr) {
-    *out_r_txt << " }\n";
+  if (r_txt_printer != nullptr) {
+    r_txt_printer->Println(" }");
   }
 
   // Add the Styleable array to the Styleable class.
@@ -396,9 +398,9 @@
     attr_processor->AppendComment(
         StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
 
-    if (out_r_txt != nullptr) {
-      *out_r_txt << StringPrintf("int styleable %s %d\n", sorted_attributes[i].field_name.data(),
-                                 (int)i);
+    if (r_txt_printer != nullptr) {
+      r_txt_printer->Println(
+          StringPrintf("int styleable %s %zd", sorted_attributes[i].field_name.c_str(), i));
     }
 
     out_class_def->AddMember(std::move(index_member));
@@ -422,10 +424,12 @@
 void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
                                          const ResourceEntry& entry, ClassDefinition* out_class_def,
                                          MethodDefinition* out_rewrite_method,
-                                         std::ostream* out_r_txt) {
+                                         text::Printer* r_txt_printer) {
   ResourceId real_id = id;
   if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
       id.package_id() > kAppPackageId) {
+    // Workaround for feature splits using package IDs > 0x7F.
+    // See b/37498913.
     real_id = ResourceId(kAppPackageId, id.package_id(), id.entry_id());
   }
 
@@ -456,8 +460,13 @@
 
   out_class_def->AddMember(std::move(resource_member));
 
-  if (out_r_txt != nullptr) {
-    *out_r_txt << "int " << name.type << " " << field_name << " " << real_id << "\n";
+  if (r_txt_printer != nullptr) {
+    r_txt_printer->Print("int ")
+        .Print(to_string(name.type))
+        .Print(" ")
+        .Print(field_name)
+        .Print(" ")
+        .Println(real_id.to_string());
   }
 
   if (out_rewrite_method != nullptr) {
@@ -497,7 +506,7 @@
                                      const ResourceTableType& type,
                                      ClassDefinition* out_type_class_def,
                                      MethodDefinition* out_rewrite_method_def,
-                                     std::ostream* out_r_txt) {
+                                     Printer* r_txt_printer) {
   for (const auto& entry : type.entries) {
     const Maybe<std::string> unmangled_name =
         UnmangleResource(package.name, package_name_to_generate, *entry);
@@ -532,18 +541,18 @@
           static_cast<const Styleable*>(entry->values.front()->value.get());
 
       ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
-                       out_rewrite_method_def, out_r_txt);
+                       out_rewrite_method_def, r_txt_printer);
     } else {
       ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
-                      out_r_txt);
+                      r_txt_printer);
     }
   }
   return true;
 }
 
-bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, std::ostream* out,
-                                  std::ostream* out_r_txt) {
-  return Generate(package_name_to_generate, package_name_to_generate, out);
+bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, OutputStream* out,
+                                  OutputStream* out_r_txt) {
+  return Generate(package_name_to_generate, package_name_to_generate, out, out_r_txt);
 }
 
 static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
@@ -556,11 +565,16 @@
 }
 
 bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
-                                  const StringPiece& out_package_name, std::ostream* out,
-                                  std::ostream* out_r_txt) {
+                                  const StringPiece& out_package_name, OutputStream* out,
+                                  OutputStream* out_r_txt) {
   ClassDefinition r_class("R", ClassQualifier::kNone, true);
   std::unique_ptr<MethodDefinition> rewrite_method;
 
+  std::unique_ptr<Printer> r_txt_printer;
+  if (out_r_txt != nullptr) {
+    r_txt_printer = util::make_unique<Printer>(out_r_txt);
+  }
+
   // Generate an onResourcesLoaded() callback if requested.
   if (options_.rewrite_callback_options) {
     rewrite_method =
@@ -586,7 +600,7 @@
       std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
           to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
       if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
-                       rewrite_method.get(), out_r_txt)) {
+                       rewrite_method.get(), r_txt_printer.get())) {
         return false;
       }
 
@@ -595,7 +609,7 @@
         const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
         if (priv_type) {
           if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
-                           rewrite_method.get(), out_r_txt)) {
+                           rewrite_method.get(), r_txt_printer.get())) {
             return false;
           }
         }
@@ -619,22 +633,7 @@
   }
 
   AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
-
-  if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out)) {
-    return false;
-  }
-
-  out->flush();
-
-  if (out_r_txt != nullptr) {
-    out_r_txt->flush();
-
-    if (!*out_r_txt) {
-      error_ = android::base::SystemErrorCodeToString(errno);
-      return false;
-    }
-  }
-
+  ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out);
   return true;
 }
 
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 2541749..4992f07 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -17,16 +17,16 @@
 #ifndef AAPT_JAVA_CLASS_GENERATOR_H
 #define AAPT_JAVA_CLASS_GENERATOR_H
 
-#include <ostream>
 #include <string>
 
 #include "androidfw/StringPiece.h"
 
 #include "ResourceTable.h"
 #include "ResourceValues.h"
-#include "androidfw/StringPiece.h"
+#include "io/Io.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
+#include "text/Printer.h"
 
 namespace aapt {
 
@@ -70,14 +70,14 @@
   // All symbols technically belong to a single package, but linked libraries will
   // have their names mangled, denoting that they came from a different package.
   // We need to generate these symbols in a separate file. Returns true on success.
-  bool Generate(const android::StringPiece& package_name_to_generate, std::ostream* out,
-                std::ostream* out_r_txt = nullptr);
+  bool Generate(const android::StringPiece& package_name_to_generate, io::OutputStream* out,
+                io::OutputStream* out_r_txt = nullptr);
 
   bool Generate(const android::StringPiece& package_name_to_generate,
-                const android::StringPiece& output_package_name, std::ostream* out,
-                std::ostream* out_r_txt = nullptr);
+                const android::StringPiece& output_package_name, io::OutputStream* out,
+                io::OutputStream* out_r_txt = nullptr);
 
-  const std::string& getError() const;
+  const std::string& GetError() const;
 
   static std::string TransformToFieldName(const android::StringPiece& symbol);
 
@@ -94,13 +94,13 @@
   bool ProcessType(const android::StringPiece& package_name_to_generate,
                    const ResourceTablePackage& package, const ResourceTableType& type,
                    ClassDefinition* out_type_class_def, MethodDefinition* out_rewrite_method_def,
-                   std::ostream* out_r_txt);
+                   text::Printer* r_txt_printer);
 
   // Writes a resource to the R.java file, optionally writing out a rewrite rule for its package
   // ID if `out_rewrite_method` is not nullptr.
   void ProcessResource(const ResourceNameRef& name, const ResourceId& id,
                        const ResourceEntry& entry, ClassDefinition* out_class_def,
-                       MethodDefinition* out_rewrite_method, std::ostream* out_r_txt);
+                       MethodDefinition* out_rewrite_method, text::Printer* r_txt_printer);
 
   // Writes a styleable resource to the R.java file, optionally writing out a rewrite rule for
   // its package ID if `out_rewrite_method` is not nullptr.
@@ -109,7 +109,7 @@
                         const Styleable& styleable,
                         const android::StringPiece& package_name_to_generate,
                         ClassDefinition* out_class_def, MethodDefinition* out_rewrite_method,
-                        std::ostream* out_r_txt);
+                        text::Printer* r_txt_printer);
 
   IAaptContext* context_;
   ResourceTable* table_;
@@ -117,7 +117,7 @@
   std::string error_;
 };
 
-inline const std::string& JavaClassGenerator::getError() const {
+inline const std::string& JavaClassGenerator::GetError() const {
   return error_;
 }
 
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 668e434..02f4cb1 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -16,12 +16,13 @@
 
 #include "java/JavaClassGenerator.h"
 
-#include <sstream>
 #include <string>
 
+#include "io/StringStream.h"
 #include "test/Test.h"
 #include "util/Util.h"
 
+using ::aapt::io::StringOutputStream;
 using ::android::StringPiece;
 using ::testing::HasSubstr;
 using ::testing::Lt;
@@ -45,7 +46,8 @@
           .Build();
   JavaClassGenerator generator(context.get(), table.get(), {});
 
-  std::stringstream out;
+  std::string result;
+  StringOutputStream out(&result);
   EXPECT_FALSE(generator.Generate("android", &out));
 }
 
@@ -69,10 +71,10 @@
           .Build();
   JavaClassGenerator generator(context.get(), table.get(), {});
 
-  std::stringstream out;
+  std::string output;
+  StringOutputStream out(&output);
   EXPECT_TRUE(generator.Generate("android", &out));
-
-  std::string output = out.str();
+  out.Flush();
 
   EXPECT_THAT(output, HasSubstr("public static final int hey_man=0x01020000;"));
   EXPECT_THAT(output, HasSubstr("public static final int[] hey_dude={"));
@@ -93,10 +95,12 @@
           .SetNameManglerPolicy(NameManglerPolicy{"android"})
           .Build();
   JavaClassGenerator generator(context.get(), table.get(), {});
-  std::stringstream out;
-  ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
 
-  std::string output = out.str();
+  std::string output;
+  StringOutputStream out(&output);
+  ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
+  out.Flush();
+
   EXPECT_THAT(output, HasSubstr("package com.android.internal;"));
   EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
   EXPECT_THAT(output, Not(HasSubstr("two")));
@@ -117,10 +121,12 @@
           .SetNameManglerPolicy(NameManglerPolicy{"android"})
           .Build();
   JavaClassGenerator generator(context.get(), table.get(), {});
-  std::stringstream out;
-  ASSERT_TRUE(generator.Generate("android", &out));
 
-  std::string output = out.str();
+  std::string output;
+  StringOutputStream out(&output);
+  ASSERT_TRUE(generator.Generate("android", &out));
+  out.Flush();
+
   EXPECT_THAT(output, HasSubstr("public static final class attr"));
   EXPECT_THAT(output, Not(HasSubstr("public static final class ^attr-private")));
 }
@@ -147,9 +153,11 @@
   options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
   {
     JavaClassGenerator generator(context.get(), table.get(), options);
-    std::stringstream out;
+    std::string output;
+    StringOutputStream out(&output);
     ASSERT_TRUE(generator.Generate("android", &out));
-    std::string output = out.str();
+    out.Flush();
+
     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
     EXPECT_THAT(output, Not(HasSubstr("two")));
     EXPECT_THAT(output, Not(HasSubstr("three")));
@@ -158,9 +166,11 @@
   options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
   {
     JavaClassGenerator generator(context.get(), table.get(), options);
-    std::stringstream out;
+    std::string output;
+    StringOutputStream out(&output);
     ASSERT_TRUE(generator.Generate("android", &out));
-    std::string output = out.str();
+    out.Flush();
+
     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
     EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
     EXPECT_THAT(output, Not(HasSubstr("three")));
@@ -169,9 +179,11 @@
   options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
   {
     JavaClassGenerator generator(context.get(), table.get(), options);
-    std::stringstream out;
+    std::string output;
+    StringOutputStream out(&output);
     ASSERT_TRUE(generator.Generate("android", &out));
-    std::string output = out.str();
+    out.Flush();
+
     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
     EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
     EXPECT_THAT(output, HasSubstr("public static final int three=0x01020002;"));
@@ -235,10 +247,11 @@
           .Build();
   JavaClassGenerator generator(context.get(), table.get(), {});
 
-  std::stringstream out;
+  std::string output;
+  StringOutputStream out(&output);
   EXPECT_TRUE(generator.Generate("android", &out));
+  out.Flush();
 
-  std::string output = out.str();
   EXPECT_THAT(output, HasSubstr("int foo_bar="));
   EXPECT_THAT(output, HasSubstr("int foo_com_lib_bar="));
 }
@@ -258,9 +271,11 @@
           .SetNameManglerPolicy(NameManglerPolicy{"android"})
           .Build();
   JavaClassGenerator generator(context.get(), table.get(), {});
-  std::stringstream out;
+
+  std::string output;
+  StringOutputStream out(&output);
   ASSERT_TRUE(generator.Generate("android", &out));
-  std::string output = out.str();
+  out.Flush();
 
   const char* expected_text =
       R"EOF(/**
@@ -298,9 +313,11 @@
   JavaClassGeneratorOptions options;
   options.use_final = false;
   JavaClassGenerator generator(context.get(), table.get(), options);
-  std::stringstream out;
+
+  std::string output;
+  StringOutputStream out(&output);
   ASSERT_TRUE(generator.Generate("android", &out));
-  std::string output = out.str();
+  out.Flush();
 
   EXPECT_THAT(output, HasSubstr("attr name android:one"));
   EXPECT_THAT(output, HasSubstr("attr description"));
@@ -332,9 +349,11 @@
 
   JavaClassGeneratorOptions options;
   JavaClassGenerator generator(context.get(), table.get(), {});
-  std::stringstream out;
+
+  std::string output;
+  StringOutputStream out(&output);
   ASSERT_TRUE(generator.Generate("android", &out));
-  std::string output = out.str();
+  out.Flush();
 
   std::string::size_type actionbar_pos = output.find("int[] ActionBar");
   ASSERT_THAT(actionbar_pos, Ne(std::string::npos));
@@ -373,9 +392,11 @@
   JavaClassGeneratorOptions options;
   options.use_final = false;
   JavaClassGenerator generator(context.get(), table.get(), options);
-  std::stringstream out;
+
+  std::string output;
+  StringOutputStream out(&output);
   ASSERT_TRUE(generator.Generate("android", &out));
-  std::string output = out.str();
+  out.Flush();
 
   EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
   EXPECT_THAT(output, Not(HasSubstr("@attr description")));
@@ -409,10 +430,10 @@
   options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
   JavaClassGenerator generator(context.get(), table.get(), options);
 
-  std::stringstream out;
+  std::string output;
+  StringOutputStream out(&output);
   ASSERT_TRUE(generator.Generate("android", &out));
-
-  std::string output = out.str();
+  out.Flush();
 
   EXPECT_THAT(output, HasSubstr("void onResourcesLoaded"));
   EXPECT_THAT(output, HasSubstr("com.foo.R.onResourcesLoaded"));
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index b12202a..3f6645f 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -23,8 +23,7 @@
 
 namespace aapt {
 
-std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
-                                                       xml::XmlResource* res);
+std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
 
 }  // namespace aapt
 
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index ada5634..c324238 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -16,8 +16,10 @@
 
 #include "java/ManifestClassGenerator.h"
 
+#include "io/StringStream.h"
 #include "test/Test.h"
 
+using ::aapt::io::StringOutputStream;
 using ::testing::HasSubstr;
 using ::testing::Not;
 
@@ -144,12 +146,9 @@
     return ::testing::AssertionFailure() << "manifest_class == nullptr";
   }
 
-  std::stringstream out;
-  if (!manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out)) {
-    return ::testing::AssertionFailure() << "failed to write java file";
-  }
-
-  *out_str = out.str();
+  StringOutputStream out(out_str);
+  manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out);
+  out.Flush();
   return ::testing::AssertionSuccess();
 }
 
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index b214d21..132b234 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -20,14 +20,18 @@
 #include <string>
 
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 
 #include "JavaClassGenerator.h"
 #include "ResourceUtils.h"
 #include "ValueVisitor.h"
-#include "androidfw/StringPiece.h"
+#include "text/Printer.h"
 #include "util/Util.h"
 #include "xml/XmlDom.h"
 
+using ::aapt::io::OutputStream;
+using ::aapt::text::Printer;
+
 namespace aapt {
 namespace proguard {
 
@@ -326,12 +330,13 @@
   return true;
 }
 
-bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) {
+  Printer printer(out);
   for (const auto& entry : keep_set.manifest_class_set_) {
     for (const UsageLocation& location : entry.second) {
-      *out << "# Referenced at " << location.source << "\n";
+      printer.Print("# Referenced at ").Println(location.source.to_string());
     }
-    *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+    printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
   }
 
   for (const auto& entry : keep_set.conditional_class_set_) {
@@ -342,26 +347,31 @@
     }
 
     for (const UsageLocation& location : entry.second) {
-      *out << "# Referenced at " << location.source << "\n";
+      printer.Print("# Referenced at ").Println(location.source.to_string());
     }
     if (keep_set.conditional_keep_rules_ && can_be_conditional) {
-      *out << "-if class **.R$layout {\n";
+      printer.Println("-if class **.R$layout {");
+      printer.Indent();
       for (const UsageLocation& location : locations) {
-        auto transformed_name = JavaClassGenerator::TransformToFieldName(location.name.entry);
-        *out << "  int " << transformed_name << ";\n";
+        printer.Print("int ")
+            .Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
+            .Println(";");
       }
-      *out << "}\n";
+      printer.Undent();
+      printer.Println("}");
+      printer.Println();
     }
-    *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+    printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+    printer.Println();
   }
 
   for (const auto& entry : keep_set.method_set_) {
     for (const UsageLocation& location : entry.second) {
-      *out << "# Referenced at " << location.source << "\n";
+      printer.Print("# Referenced at ").Println(location.source.to_string());
     }
-    *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
+    printer.Print("-keepclassmembers class * { *** ").Print(entry.first).Println("(...); }");
+    printer.Println();
   }
-  return true;
 }
 
 bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 8dbe3c2..46827ee 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -22,11 +22,13 @@
 #include <set>
 #include <string>
 
+#include "androidfw/StringPiece.h"
+
 #include "Resource.h"
 #include "ResourceTable.h"
 #include "Source.h"
 #include "ValueVisitor.h"
-#include "androidfw/StringPiece.h"
+#include "io/Io.h"
 #include "process/IResourceTableConsumer.h"
 #include "xml/XmlDom.h"
 
@@ -62,7 +64,7 @@
   }
 
  private:
-  friend bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
+  friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
 
   friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                                std::set<UsageLocation>* locations);
@@ -76,11 +78,12 @@
 
 bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set,
                                      bool main_dex_only = false);
-bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set);
-bool CollectResourceReferences(aapt::IAaptContext* context, ResourceTable* table,
-                               KeepSet* keep_set);
 
-bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
+bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set);
+
+bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
+
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
 
 bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                       std::set<UsageLocation>* locations);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 802c56a..37d1a5f 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -17,13 +17,23 @@
 #include "java/ProguardRules.h"
 #include "link/Linkers.h"
 
+#include "io/StringStream.h"
 #include "test/Test.h"
 
+using ::aapt::io::StringOutputStream;
 using ::testing::HasSubstr;
 using ::testing::Not;
 
 namespace aapt {
 
+std::string GetKeepSetString(const proguard::KeepSet& set) {
+  std::string out;
+  StringOutputStream sout(&out);
+  proguard::WriteKeepSet(set, &sout);
+  sout.Flush();
+  return out;
+}
+
 TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
@@ -34,10 +44,8 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
   EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
 }
 
@@ -50,10 +58,8 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
   EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
 }
 
@@ -68,10 +74,8 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
   EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
   EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
 }
@@ -87,10 +91,8 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
   EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
 }
 
@@ -126,11 +128,10 @@
   ASSERT_TRUE(proguard::CollectProguardRules(bar_layout.get(), &set));
   ASSERT_TRUE(proguard::CollectProguardRules(foo_layout.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
   EXPECT_THAT(actual, HasSubstr("int foo"));
   EXPECT_THAT(actual, HasSubstr("int bar"));
   EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -148,10 +149,9 @@
   set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
   ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
   EXPECT_THAT(actual, HasSubstr("int foo"));
   EXPECT_THAT(actual, HasSubstr("int bar"));
@@ -170,11 +170,10 @@
   set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
   ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
   EXPECT_THAT(actual, Not(HasSubstr("-if")));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
 }
 
 TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
@@ -187,10 +186,8 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
   EXPECT_THAT(actual, HasSubstr("bar_method"));
 }
 
@@ -208,10 +205,8 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(menu.get(), &set));
 
-  std::stringstream out;
-  ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+  std::string actual = GetKeepSetString(set);
 
-  std::string actual = out.str();
   EXPECT_THAT(actual, HasSubstr("on_click"));
   EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
   EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
diff --git a/tools/aapt2/text/Printer.cpp b/tools/aapt2/text/Printer.cpp
index 38b3585..243800c 100644
--- a/tools/aapt2/text/Printer.cpp
+++ b/tools/aapt2/text/Printer.cpp
@@ -26,18 +26,18 @@
 namespace aapt {
 namespace text {
 
-void Printer::Println(const StringPiece& str) {
+Printer& Printer::Println(const StringPiece& str) {
   Print(str);
-  Print("\n");
+  return Print("\n");
 }
 
-void Printer::Println() {
-  Print("\n");
+Printer& Printer::Println() {
+  return Print("\n");
 }
 
-void Printer::Print(const StringPiece& str) {
+Printer& Printer::Print(const StringPiece& str) {
   if (error_) {
-    return;
+    return *this;
   }
 
   auto remaining_str_begin = str.begin();
@@ -53,7 +53,7 @@
         for (int i = 0; i < indent_level_; i++) {
           if (!io::Copy(out_, "  ")) {
             error_ = true;
-            return;
+            return *this;
           }
         }
         needs_indent_ = false;
@@ -61,7 +61,7 @@
 
       if (!io::Copy(out_, str_to_copy)) {
         error_ = true;
-        return;
+        return *this;
       }
     }
 
@@ -69,7 +69,7 @@
     if (new_line_iter != remaining_str_end) {
       if (!io::Copy(out_, "\n")) {
         error_ = true;
-        return;
+        return *this;
       }
       needs_indent_ = true;
       // Ok to increment iterator here because we know that the '\n' character is one byte.
@@ -78,6 +78,7 @@
       remaining_str_begin = new_line_iter;
     }
   }
+  return *this;
 }
 
 void Printer::Indent() {
diff --git a/tools/aapt2/text/Printer.h b/tools/aapt2/text/Printer.h
index 94b3c0b..f399f8e 100644
--- a/tools/aapt2/text/Printer.h
+++ b/tools/aapt2/text/Printer.h
@@ -31,9 +31,9 @@
   explicit Printer(::aapt::io::OutputStream* out) : out_(out) {
   }
 
-  void Print(const ::android::StringPiece& str);
-  void Println(const ::android::StringPiece& str);
-  void Println();
+  Printer& Print(const ::android::StringPiece& str);
+  Printer& Println(const ::android::StringPiece& str);
+  Printer& Println();
 
   void Indent();
   void Undent();
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_linter.py
similarity index 100%
rename from tools/fonts/fontchain_lint.py
rename to tools/fonts/fontchain_linter.py
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index c54ab18..cca1294 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -107,6 +107,8 @@
 
     fprintf(out, "namespace android {\n");
     fprintf(out, "namespace util {\n");
+    fprintf(out, "// the single event tag id for all stats logs\n");
+    fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
 
     // Print write methods
     fprintf(out, "\n");
@@ -115,7 +117,7 @@
         int argIndex;
 
         fprintf(out, "void\n");
-        fprintf(out, "stats_write(int code");
+        fprintf(out, "stats_write(int32_t code");
         argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
@@ -126,7 +128,8 @@
 
         fprintf(out, "{\n");
         argIndex = 1;
-        fprintf(out, "    android_log_event_list event(code);\n");
+        fprintf(out, "    android_log_event_list event(kStatsEventTag);\n");
+        fprintf(out, "    event << code;\n");
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
             if (*arg == JAVA_TYPE_STRING) {
@@ -204,8 +207,7 @@
     fprintf(out, "//\n");
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
             signature != atoms.signatures.end(); signature++) {
-
-        fprintf(out, "void stats_write(int code");
+        fprintf(out, "void stats_write(int32_t code ");
         int argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index 115b86d..aa2c268 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -113,6 +113,32 @@
     }
 
     /**
+     * Called when a discovery (publish or subscribe) operation results in a
+     * service discovery. Called when a Subscribe service was configured with a range requirement
+     * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and/or
+     * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)}. A discovery will only be declared
+     * (i.e. this callback called) if the range of the publisher is within the specified distance
+     * constraints.
+     *
+     * @param peerHandle An opaque handle to the peer matching our discovery operation.
+     * @param serviceSpecificInfo The service specific information (arbitrary
+     *            byte array) provided by the peer as part of its discovery
+     *            configuration.
+     * @param matchFilter The filter which resulted in this service discovery. For
+     * {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED},
+     * {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE} discovery sessions this is the publisher's
+     *                    match filter. For {@link PublishConfig#PUBLISH_TYPE_SOLICITED},
+     *                    {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE} discovery sessions this
+     *                    is the subscriber's match filter.
+     * @param distanceMm The measured distance to the Publisher in mm.
+     * @hide
+     */
+    public void onServiceDiscoveredWithinRange(PeerHandle peerHandle,
+        byte[] serviceSpecificInfo, List<byte[]> matchFilter, int distanceMm) {
+        /* empty */
+    }
+
+    /**
      * Called in response to
      * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}
      * when a message is transmitted successfully - i.e. when it was received successfully by the
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
index 8ff3842..421a8af 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
@@ -29,6 +29,8 @@
     void onSessionTerminated(int reason);
 
     void onMatch(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter);
+    void onMatchWithDistance(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter,
+            int distanceMm);
 
     void onMessageSendSuccess(int messageId);
     void onMessageSendFail(int messageId, int reason);
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index d018620..e60f52f 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -29,6 +29,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Defines the configuration of a Aware publish session. Built using
@@ -81,14 +82,19 @@
     public final boolean mEnableTerminateNotification;
 
     /** @hide */
+    public final boolean mEnableRanging;
+
+    /** @hide */
     public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
-            int publishType, int ttlSec, boolean enableTerminateNotification) {
+            int publishType, int ttlSec, boolean enableTerminateNotification,
+            boolean enableRanging) {
         mServiceName = serviceName;
         mServiceSpecificInfo = serviceSpecificInfo;
         mMatchFilter = matchFilter;
         mPublishType = publishType;
         mTtlSec = ttlSec;
         mEnableTerminateNotification = enableTerminateNotification;
+        mEnableRanging = enableRanging;
     }
 
     @Override
@@ -103,7 +109,8 @@
                 + (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
                 + ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
                 + ", mPublishType=" + mPublishType + ", mTtlSec=" + mTtlSec
-                + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+                + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+                + ", mEnableRanging=" + mEnableRanging + "]";
     }
 
     @Override
@@ -119,6 +126,7 @@
         dest.writeInt(mPublishType);
         dest.writeInt(mTtlSec);
         dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+        dest.writeInt(mEnableRanging ? 1 : 0);
     }
 
     public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() {
@@ -135,9 +143,10 @@
             int publishType = in.readInt();
             int ttlSec = in.readInt();
             boolean enableTerminateNotification = in.readInt() != 0;
+            boolean enableRanging = in.readInt() != 0;
 
             return new PublishConfig(serviceName, ssi, matchFilter, publishType,
-                    ttlSec, enableTerminateNotification);
+                    ttlSec, enableTerminateNotification, enableRanging);
         }
     };
 
@@ -157,21 +166,14 @@
                 lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
                 && mPublishType == lhs.mPublishType
                 && mTtlSec == lhs.mTtlSec
-                && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+                && mEnableTerminateNotification == lhs.mEnableTerminateNotification
+                && mEnableRanging == lhs.mEnableRanging;
     }
 
     @Override
     public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + Arrays.hashCode(mServiceName);
-        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
-        result = 31 * result + Arrays.hashCode(mMatchFilter);
-        result = 31 * result + mPublishType;
-        result = 31 * result + mTtlSec;
-        result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
-
-        return result;
+        return Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType, mTtlSec,
+                mEnableTerminateNotification, mEnableRanging);
     }
 
     /**
@@ -226,6 +228,7 @@
         private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
         private int mTtlSec = 0;
         private boolean mEnableTerminateNotification = true;
+        private boolean mEnableRanging = false;
 
         /**
          * Specify the service name of the publish session. The actual on-air
@@ -352,12 +355,35 @@
         }
 
         /**
+         * Configure whether the publish discovery session supports ranging and allows peers to
+         * measure distance to it. This API is used in conjunction with
+         * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and
+         * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or
+         * maximum distance at which discovery will be triggered.
+         * <p>
+         * Optional. Disabled by default - i.e. any peer which attempts to measure distance to this
+         * device will be refused. If the peer has ranging enabled (using the
+         * {@link SubscribeConfig} APIs listed above, it will never discover this device.
+         *
+         * @param enable If true, ranging is supported on request of the peer.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         *
+         * @hide
+         */
+        public Builder setRangingEnabled(boolean enable) {
+            mEnableRanging = enable;
+            return this;
+        }
+
+        /**
          * Build {@link PublishConfig} given the current requests made on the
          * builder.
          */
         public PublishConfig build() {
             return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType,
-                    mTtlSec, mEnableTerminateNotification);
+                    mTtlSec, mEnableTerminateNotification, mEnableRanging);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index 4bf2fb6..f6552a7 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -29,6 +29,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Defines the configuration of a Aware subscribe session. Built using
@@ -79,15 +80,32 @@
     public final boolean mEnableTerminateNotification;
 
     /** @hide */
+    public final boolean mMinDistanceMmSet;
+
+    /** @hide */
+    public final int mMinDistanceMm;
+
+    /** @hide */
+    public final boolean mMaxDistanceMmSet;
+
+    /** @hide */
+    public final int mMaxDistanceMm;
+
+    /** @hide */
     public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
-            int subscribeType, int ttlSec,
-            boolean enableTerminateNotification) {
+            int subscribeType, int ttlSec, boolean enableTerminateNotification,
+            boolean minDistanceMmSet, int minDistanceMm, boolean maxDistanceMmSet,
+            int maxDistanceMm) {
         mServiceName = serviceName;
         mServiceSpecificInfo = serviceSpecificInfo;
         mMatchFilter = matchFilter;
         mSubscribeType = subscribeType;
         mTtlSec = ttlSec;
         mEnableTerminateNotification = enableTerminateNotification;
+        mMinDistanceMm = minDistanceMm;
+        mMinDistanceMmSet = minDistanceMmSet;
+        mMaxDistanceMm = maxDistanceMm;
+        mMaxDistanceMmSet = maxDistanceMmSet;
     }
 
     @Override
@@ -102,7 +120,11 @@
                 + (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
                 + ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
                 + ", mSubscribeType=" + mSubscribeType + ", mTtlSec=" + mTtlSec
-                + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+                + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+                + ", mMinDistanceMm=" + mMinDistanceMm
+                + ", mMinDistanceMmSet=" + mMinDistanceMmSet
+                + ", mMaxDistanceMm=" + mMaxDistanceMm
+                + ", mMaxDistanceMmSet=" + mMaxDistanceMmSet + "]";
     }
 
     @Override
@@ -118,6 +140,10 @@
         dest.writeInt(mSubscribeType);
         dest.writeInt(mTtlSec);
         dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+        dest.writeInt(mMinDistanceMm);
+        dest.writeInt(mMinDistanceMmSet ? 1 : 0);
+        dest.writeInt(mMaxDistanceMm);
+        dest.writeInt(mMaxDistanceMmSet ? 1 : 0);
     }
 
     public static final Creator<SubscribeConfig> CREATOR = new Creator<SubscribeConfig>() {
@@ -134,9 +160,14 @@
             int subscribeType = in.readInt();
             int ttlSec = in.readInt();
             boolean enableTerminateNotification = in.readInt() != 0;
+            int minDistanceMm = in.readInt();
+            boolean minDistanceMmSet = in.readInt() != 0;
+            int maxDistanceMm = in.readInt();
+            boolean maxDistanceMmSet = in.readInt() != 0;
 
-            return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType,
-                    ttlSec, enableTerminateNotification);
+            return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType, ttlSec,
+                    enableTerminateNotification, minDistanceMmSet, minDistanceMm, maxDistanceMmSet,
+                    maxDistanceMm);
         }
     };
 
@@ -152,23 +183,37 @@
 
         SubscribeConfig lhs = (SubscribeConfig) o;
 
-        return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
-                lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
-                && mSubscribeType == lhs.mSubscribeType
-                && mTtlSec == lhs.mTtlSec
-                && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+        if (!(Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(
+                mServiceSpecificInfo, lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter,
+                lhs.mMatchFilter) && mSubscribeType == lhs.mSubscribeType && mTtlSec == lhs.mTtlSec
+                && mEnableTerminateNotification == lhs.mEnableTerminateNotification
+                && mMinDistanceMmSet == lhs.mMinDistanceMmSet
+                && mMaxDistanceMmSet == lhs.mMaxDistanceMmSet)) {
+            return false;
+        }
+
+        if (mMinDistanceMmSet && mMinDistanceMm != lhs.mMinDistanceMm) {
+            return false;
+        }
+
+        if (mMaxDistanceMmSet && mMaxDistanceMm != lhs.mMaxDistanceMm) {
+            return false;
+        }
+
+        return true;
     }
 
     @Override
     public int hashCode() {
-        int result = 17;
+        int result = Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mSubscribeType,
+                mTtlSec, mEnableTerminateNotification, mMinDistanceMmSet, mMaxDistanceMmSet);
 
-        result = 31 * result + Arrays.hashCode(mServiceName);
-        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
-        result = 31 * result + Arrays.hashCode(mMatchFilter);
-        result = 31 * result + mSubscribeType;
-        result = 31 * result + mTtlSec;
-        result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
+        if (mMinDistanceMmSet) {
+            result = Objects.hash(result, mMinDistanceMm);
+        }
+        if (mMaxDistanceMmSet) {
+            result = Objects.hash(result, mMaxDistanceMm);
+        }
 
         return result;
     }
@@ -213,6 +258,17 @@
                         "Match filter longer than supported by device characteristics");
             }
         }
+
+        if (mMinDistanceMmSet && mMinDistanceMm < 0) {
+            throw new IllegalArgumentException("Minimum distance must be non-negative");
+        }
+        if (mMaxDistanceMmSet && mMaxDistanceMm < 0) {
+            throw new IllegalArgumentException("Maximum distance must be non-negative");
+        }
+        if (mMinDistanceMmSet && mMaxDistanceMmSet && mMaxDistanceMm <= mMinDistanceMm) {
+            throw new IllegalArgumentException(
+                    "Maximum distance must be greater than minimum distance");
+        }
     }
 
     /**
@@ -225,6 +281,10 @@
         private int mSubscribeType = SUBSCRIBE_TYPE_PASSIVE;
         private int mTtlSec = 0;
         private boolean mEnableTerminateNotification = true;
+        private boolean mMinDistanceMmSet = false;
+        private int mMinDistanceMm;
+        private boolean mMaxDistanceMmSet = false;
+        private int mMaxDistanceMm;
 
         /**
          * Specify the service name of the subscribe session. The actual on-air
@@ -350,13 +410,69 @@
         }
 
         /**
+         * Configure the minimum distance to a discovered publisher at which to trigger a discovery
+         * notification. I.e. discovery will only be triggered if we've found a matching publisher
+         * (based on the other criteria in this configuration) <b>and</b> the distance to the
+         * publisher is > the value specified in this API.
+         * <p>
+         * Can be used in conjunction with {@link #setMaxDistanceMm(int)} to specify a geo-fence,
+         * i.e. discovery with min < distance < max.
+         * <p>
+         * If this API is called, the subscriber requires ranging. In such a case, the publisher
+         * peer must enable ranging using
+         * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
+         * never be triggered.
+         *
+         * @param minDistanceMm Minimum distance, in mm, to the publisher above which to trigger
+         *                      discovery.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         *
+         * @hide
+         */
+        public Builder setMinDistanceMm(int minDistanceMm) {
+            mMinDistanceMm = minDistanceMm;
+            mMinDistanceMmSet = true;
+            return this;
+        }
+
+        /**
+         * Configure the maximum distance to a discovered publisher at which to trigger a discovery
+         * notification. I.e. discovery will only be triggered if we've found a matching publisher
+         * (based on the other criteria in this configuration) <b>and</b> the distance to the
+         * publisher is < the value specified in this API.
+         * <p>
+         * Can be used in conjunction with {@link #setMinDistanceMm(int)} to specify a geo-fence,
+         * i.e. discovery with min < distance < max.
+         * <p>
+         * If this API is called, the subscriber requires ranging. In such a case, the publisher
+         * peer must enable ranging using
+         * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
+         * never be triggered.
+         *
+         * @param maxDistanceMm Maximum distance, in mm, to the publisher below which to trigger
+         *                      discovery.
+         *
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         *
+         * @hide
+         */
+        public Builder setMaxDistanceMm(int maxDistanceMm) {
+            mMaxDistanceMm = maxDistanceMm;
+            mMaxDistanceMmSet = true;
+            return this;
+        }
+
+        /**
          * Build {@link SubscribeConfig} given the current requests made on the
          * builder.
          */
         public SubscribeConfig build() {
             return new SubscribeConfig(mServiceName, mServiceSpecificInfo, mMatchFilter,
-                    mSubscribeType, mTtlSec,
-                    mEnableTerminateNotification);
+                    mSubscribeType, mTtlSec, mEnableTerminateNotification,
+                    mMinDistanceMmSet, mMinDistanceMm, mMaxDistanceMmSet, mMaxDistanceMm);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index ed6804d..166da48 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -20,8 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
-import android.annotation.SystemService;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkRequest;
@@ -564,6 +564,7 @@
         private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 5;
         private static final int CALLBACK_MESSAGE_SEND_FAIL = 6;
         private static final int CALLBACK_MESSAGE_RECEIVED = 7;
+        private static final int CALLBACK_MATCH_WITH_DISTANCE = 8;
 
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
@@ -618,7 +619,9 @@
                         case CALLBACK_SESSION_TERMINATED:
                             onProxySessionTerminated(msg.arg1);
                             break;
-                        case CALLBACK_MATCH: {
+                        case CALLBACK_MATCH:
+                        case CALLBACK_MATCH_WITH_DISTANCE:
+                            {
                             List<byte[]> matchFilter = null;
                             byte[] arg = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2);
                             try {
@@ -629,9 +632,16 @@
                                         + new String(HexEncoding.encode(arg))
                                         + "' - cannot be parsed: e=" + e);
                             }
-                            mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
-                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
-                                    matchFilter);
+                            if (msg.what == CALLBACK_MATCH) {
+                                mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
+                                        msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+                                        matchFilter);
+                            } else {
+                                mOriginalCallback.onServiceDiscoveredWithinRange(
+                                        new PeerHandle(msg.arg1),
+                                        msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+                                        matchFilter, msg.arg2);
+                            }
                             break;
                         }
                         case CALLBACK_MESSAGE_SEND_SUCCESS:
@@ -684,21 +694,38 @@
             mHandler.sendMessage(msg);
         }
 
-        @Override
-        public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
-            if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
-
+        private void onMatchCommon(int messageType, int peerId, byte[] serviceSpecificInfo,
+                byte[] matchFilter, int distanceMm) {
             Bundle data = new Bundle();
             data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
             data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
 
-            Message msg = mHandler.obtainMessage(CALLBACK_MATCH);
+            Message msg = mHandler.obtainMessage(messageType);
             msg.arg1 = peerId;
+            msg.arg2 = distanceMm;
             msg.setData(data);
             mHandler.sendMessage(msg);
         }
 
         @Override
+        public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
+            if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
+
+            onMatchCommon(CALLBACK_MATCH, peerId, serviceSpecificInfo, matchFilter, 0);
+        }
+
+        @Override
+        public void onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter,
+                int distanceMm) {
+            if (VDBG) {
+                Log.v(TAG, "onMatchWithDistance: peerId=" + peerId + ", distanceMm=" + distanceMm);
+            }
+
+            onMatchCommon(CALLBACK_MATCH_WITH_DISTANCE, peerId, serviceSpecificInfo, matchFilter,
+                    distanceMm);
+        }
+
+        @Override
         public void onMessageSendSuccess(int messageId) {
             if (VDBG) Log.v(TAG, "onMessageSendSuccess");
 
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index 128d6c9..735e872 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -44,7 +44,7 @@
 @SystemService(Context.WIFI_RTT2_SERVICE)
 public class WifiRttManager {
     private static final String TAG = "WifiRttManager";
-    private static final boolean VDBG = true;
+    private static final boolean VDBG = false;
 
     private final Context mContext;
     private final IWifiRttManager mService;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 1aeeee3..653fcff 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -19,20 +19,17 @@
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.net.wifi.RttManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcel;
-import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -407,6 +404,7 @@
         final byte[] matchFilter = { 1, 12, 3, 31, 32 }; // bad data!
         final int messageId = 2123;
         final int reason = AWARE_STATUS_ERROR;
+        final int distanceMm = 100;
 
         InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
                 mockSubscribeSession);
@@ -442,6 +440,8 @@
         // (3) ...
         subscribeSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
         sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter);
+        sessionProxyCallback.getValue().onMatchWithDistance(peerHandle.peerId, string1.getBytes(),
+                matchFilter, distanceMm);
         sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
         sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
         sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
@@ -450,7 +450,9 @@
         inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
                 eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
         inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
-                eq(string1.getBytes()), (List<byte[]>) isNull());
+                eq(string1.getBytes()), isNull());
+        inOrder.verify(mockSessionCallback).onServiceDiscoveredWithinRange(peerIdCaptor.capture(),
+                eq(string1.getBytes()), isNull(), eq(distanceMm));
         assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId);
         inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
                 eq(string1.getBytes()));
@@ -685,11 +687,18 @@
         SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
 
         collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
+        collector.checkThat("mServiceSpecificInfo", subscribeConfig.mServiceSpecificInfo,
+                equalTo(null));
+        collector.checkThat("mMatchFilter", subscribeConfig.mMatchFilter, equalTo(null));
         collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
                 equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
         collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
         collector.checkThat("mEnableTerminateNotification",
                 subscribeConfig.mEnableTerminateNotification, equalTo(true));
+        collector.checkThat("mMinDistanceCmSet", subscribeConfig.mMinDistanceMmSet, equalTo(false));
+        collector.checkThat("mMinDistanceMm", subscribeConfig.mMinDistanceMm, equalTo(0));
+        collector.checkThat("mMaxDistanceMmSet", subscribeConfig.mMaxDistanceMmSet, equalTo(false));
+        collector.checkThat("mMaxDistanceMm", subscribeConfig.mMaxDistanceMm, equalTo(0));
     }
 
     @Test
@@ -698,16 +707,19 @@
         final String serviceSpecificInfo = "long arbitrary string with some info";
         final byte[] matchFilter = { 1, 16, 1, 22 };
         final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
-        final int subscribeCount = 10;
         final int subscribeTtl = 15;
         final boolean enableTerminateNotification = false;
+        final int minDistance = 10;
+        final int maxDistance = 50;
 
         SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
                 .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
-                        new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+                    new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
                 .setSubscribeType(subscribeType)
                 .setTtlSec(subscribeTtl)
-                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+                .setTerminateNotificationEnabled(enableTerminateNotification)
+                .setMinDistanceMm(minDistance)
+                .setMaxDistanceMm(maxDistance).build();
 
         collector.checkThat("mServiceName", serviceName.getBytes(),
                 equalTo(subscribeConfig.mServiceName));
@@ -719,6 +731,10 @@
         collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
         collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
                 equalTo(subscribeConfig.mEnableTerminateNotification));
+        collector.checkThat("mMinDistanceMmSet", true, equalTo(subscribeConfig.mMinDistanceMmSet));
+        collector.checkThat("mMinDistanceMm", minDistance, equalTo(subscribeConfig.mMinDistanceMm));
+        collector.checkThat("mMaxDistanceMmSet", true, equalTo(subscribeConfig.mMaxDistanceMmSet));
+        collector.checkThat("mMaxDistanceMm", maxDistance, equalTo(subscribeConfig.mMaxDistanceMm));
     }
 
     @Test
@@ -729,13 +745,17 @@
         final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
         final int subscribeTtl = 15;
         final boolean enableTerminateNotification = true;
+        final int minDistance = 10;
+        final int maxDistance = 50;
 
         SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
                 .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
                         new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
                 .setSubscribeType(subscribeType)
                 .setTtlSec(subscribeTtl)
-                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+                .setTerminateNotificationEnabled(enableTerminateNotification)
+                .setMinDistanceMm(minDistance)
+                .setMaxDistanceMm(maxDistance).build();
 
         Parcel parcelW = Parcel.obtain();
         subscribeConfig.writeToParcel(parcelW, 0);
@@ -769,11 +789,15 @@
         PublishConfig publishConfig = new PublishConfig.Builder().build();
 
         collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
+        collector.checkThat("mServiceSpecificInfo", publishConfig.mServiceSpecificInfo,
+                equalTo(null));
+        collector.checkThat("mMatchFilter", publishConfig.mMatchFilter, equalTo(null));
         collector.checkThat("mPublishType", publishConfig.mPublishType,
                 equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
         collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
         collector.checkThat("mEnableTerminateNotification",
                 publishConfig.mEnableTerminateNotification, equalTo(true));
+        collector.checkThat("mEnableRanging", publishConfig.mEnableRanging, equalTo(false));
     }
 
     @Test
@@ -782,16 +806,17 @@
         final String serviceSpecificInfo = "long arbitrary string with some info";
         final byte[] matchFilter = { 1, 16, 1, 22 };
         final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
-        final int publishCount = 10;
         final int publishTtl = 15;
         final boolean enableTerminateNotification = false;
+        final boolean enableRanging = true;
 
         PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
                 .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
                         new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
                 .setPublishType(publishType)
                 .setTtlSec(publishTtl)
-                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+                .setTerminateNotificationEnabled(enableTerminateNotification)
+                .setRangingEnabled(enableRanging).build();
 
         collector.checkThat("mServiceName", serviceName.getBytes(),
                 equalTo(publishConfig.mServiceName));
@@ -802,6 +827,7 @@
         collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
         collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
                 equalTo(publishConfig.mEnableTerminateNotification));
+        collector.checkThat("mEnableRanging", enableRanging, equalTo(publishConfig.mEnableRanging));
     }
 
     @Test
@@ -810,16 +836,17 @@
         final String serviceSpecificInfo = "long arbitrary string with some info";
         final byte[] matchFilter = { 1, 16, 1, 22 };
         final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
-        final int publishCount = 10;
         final int publishTtl = 15;
         final boolean enableTerminateNotification = false;
+        final boolean enableRanging = true;
 
         PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
                 .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
                         new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
                 .setPublishType(publishType)
                 .setTtlSec(publishTtl)
-                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+                .setTerminateNotificationEnabled(enableTerminateNotification)
+                .setRangingEnabled(enableRanging).build();
 
         Parcel parcelW = Parcel.obtain();
         publishConfig.writeToParcel(parcelW, 0);