Merge "Add pushing many batterystats events to statsd"
diff --git a/Android.mk b/Android.mk
index 6470e34..c87aec4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -425,7 +425,6 @@
 	core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl \
 	core/java/com/android/internal/widget/ILockSettings.aidl \
 	core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
-	core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
 	keystore/java/android/security/IKeyChainAliasCallback.aidl \
 	keystore/java/android/security/IKeyChainService.aidl \
 	location/java/android/location/IBatchedLocationCallback.aidl \
@@ -579,6 +578,8 @@
 
 LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
 
+LOCAL_AIDL_INCLUDES += core/java/android/os/StatsLogEventWrapper.aidl
+
 LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java
 LOCAL_SRC_FILES += \
 	lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl \
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 200f92f..f08b402 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -4,11 +4,14 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+  $(call all-java-files-under, src) \
+  src/android/os/ISomeService.aidl
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     apct-perftests-utils \
+    guava \
     legacy-android-test
 
 LOCAL_PACKAGE_NAME := CorePerfTests
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index a709ee3..f27e8c8 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -5,6 +5,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
         <activity android:name="android.perftests.utils.StubActivity" />
+        <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/apct-tests/perftests/core/src/android/os/ISomeService.aidl b/apct-tests/perftests/core/src/android/os/ISomeService.aidl
new file mode 100644
index 0000000..0658b69
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/ISomeService.aidl
@@ -0,0 +1,5 @@
+package android.os;
+
+interface ISomeService {
+    void readDisk(int times);
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/os/SomeService.java b/apct-tests/perftests/core/src/android/os/SomeService.java
new file mode 100644
index 0000000..bdfaa44
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/SomeService.java
@@ -0,0 +1,42 @@
+package android.os;
+
+import android.app.Service;
+import android.content.Intent;
+import java.io.File;
+import java.io.IOException;
+
+/** Service in separate process available for calling over binder. */
+public class SomeService extends Service {
+
+    private File mTempFile;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        try {
+            mTempFile = File.createTempFile("foo", "bar");
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private final ISomeService.Stub mBinder =
+            new ISomeService.Stub() {
+                public void readDisk(int times) {
+                    for (int i = 0; i < times; i++) {
+                        mTempFile.exists();
+                    }
+                }
+            };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mTempFile.delete();
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/os/StrictModeTest.java b/apct-tests/perftests/core/src/android/os/StrictModeTest.java
new file mode 100644
index 0000000..d973c20
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/StrictModeTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import com.google.common.util.concurrent.SettableFuture;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StrictModeTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeVmViolation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
+        causeVmViolations(state);
+    }
+
+    @Test
+    public void timeVmViolationNoStrictMode() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        causeVmViolations(state);
+    }
+
+    private static void causeVmViolations(BenchmarkState state) {
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setDataAndType(Uri.parse("content://com.example/foobar"), "image/jpeg");
+        final Context context = InstrumentationRegistry.getTargetContext();
+        while (state.keepRunning()) {
+            context.startActivity(intent);
+        }
+    }
+
+    @Test
+    public void timeThreadViolation() throws IOException {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        StrictMode.setThreadPolicy(
+                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+        causeThreadViolations(state);
+    }
+
+    @Test
+    public void timeThreadViolationNoStrictMode() throws IOException {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        causeThreadViolations(state);
+    }
+
+    private static void causeThreadViolations(BenchmarkState state) throws IOException {
+        final File test = File.createTempFile("foo", "bar");
+        while (state.keepRunning()) {
+            test.exists();
+        }
+        test.delete();
+    }
+
+    @Test
+    public void timeCrossBinderThreadViolation() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        StrictMode.setThreadPolicy(
+                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+        causeCrossProcessThreadViolations(state);
+    }
+
+    @Test
+    public void timeCrossBinderThreadViolationNoStrictMode() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        causeCrossProcessThreadViolations(state);
+    }
+
+    private static void causeCrossProcessThreadViolations(BenchmarkState state)
+            throws ExecutionException, InterruptedException, RemoteException {
+        final Context context = InstrumentationRegistry.getTargetContext();
+
+        SettableFuture<IBinder> binder = SettableFuture.create();
+        ServiceConnection connection =
+                new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName className, IBinder service) {
+                        binder.set(service);
+                    }
+
+                    @Override
+                    public void onServiceDisconnected(ComponentName arg0) {
+                        binder.set(null);
+                    }
+                };
+        context.bindService(
+                new Intent(context, SomeService.class), connection, Context.BIND_AUTO_CREATE);
+        ISomeService someService = ISomeService.Stub.asInterface(binder.get());
+        while (state.keepRunning()) {
+            // Violate strictmode heavily.
+            someService.readDisk(10);
+        }
+        context.unbindService(connection);
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index df8b7ef..8b3c740 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6451,6 +6451,8 @@
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+    method public boolean setTime(android.content.ComponentName, long);
+    method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
     method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/api/system-current.txt b/api/system-current.txt
index 007316f..ee23b6d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6686,6 +6686,8 @@
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+    method public boolean setTime(android.content.ComponentName, long);
+    method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
     method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/api/test-current.txt b/api/test-current.txt
index faccb6b..9a241ad 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6519,6 +6519,8 @@
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+    method public boolean setTime(android.content.ComponentName, long);
+    method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
     method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 3c2f2d5..8946aed 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -37,6 +37,7 @@
     src/matchers/matcher_util.cpp \
     src/matchers/SimpleLogMatchingTracker.cpp \
     src/metrics/CountAnomalyTracker.cpp \
+    src/metrics/MetricProducer.cpp \
     src/metrics/CountMetricProducer.cpp \
     src/metrics/DurationMetricProducer.cpp \
     src/metrics/MetricsManager.cpp \
@@ -49,7 +50,8 @@
     src/stats_util.cpp
 
 statsd_common_c_includes := \
-    $(LOCAL_PATH)/src
+    $(LOCAL_PATH)/src \
+    $(LOCAL_PATH)/../../libs/services/include
 
 statsd_common_aidl_includes := \
     $(LOCAL_PATH)/../../core/java
@@ -95,7 +97,7 @@
 endif
 LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
 
-LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
+LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
 LOCAL_C_INCLUDES += $(statsd_common_c_includes)
 
 LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
@@ -117,7 +119,7 @@
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
+LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
 LOCAL_C_INCLUDES += $(statsd_common_c_includes)
 
 LOCAL_CFLAGS += \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 87616d3..1faeee0 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -63,9 +63,9 @@
 
 // ======================================================================
 StatsService::StatsService(const sp<Looper>& handlerLooper)
-    : mStatsPullerManager(),
-      mAnomalyMonitor(new AnomalyMonitor(2))  // TODO: Put this comment somewhere better
+    : mAnomalyMonitor(new AnomalyMonitor(2))  // TODO: Put this comment somewhere better
 {
+    mStatsPullerManager = new StatsPullerManager();
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
     mProcessor = new StatsLogProcessor(mUidMap);
@@ -193,6 +193,10 @@
         if (!args[0].compare(String8("dump-report"))) {
             return cmd_dump_report(out, err, args);
         }
+
+        if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
+            return cmd_print_pulled_metrics(out, args);
+        }
     }
 
     print_cmd_help(out);
@@ -210,6 +214,11 @@
     fprintf(out, "  Prints the UID, app name, version mapping.\n");
     fprintf(out, "\n");
     fprintf(out, "\n");
+    fprintf(out, "usage: adb shell cmds 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");
+    fprintf(out, "\n");
     fprintf(out, "usage: adb shell cmd stats config remove [UID] NAME\n");
     fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
     fprintf(out, "\n");
@@ -353,6 +362,16 @@
     return NO_ERROR;
 }
 
+status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
+    int s = atoi(args[1].c_str());
+    auto stats = mStatsPullerManager->Pull(s);
+    for (const auto& it : stats) {
+        fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
+    }
+    fprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
+    return NO_ERROR;
+}
+
 Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
                                       const vector<String16>& app) {
     if (DEBUG) ALOGD("StatsService::informAllUidData was called");
@@ -414,10 +433,6 @@
 
     if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
     // TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
-    String16 output = mStatsPullerManager.pull(StatsPullerManager::KERNEL_WAKELOCKS);
-    // TODO: do something useful with the output instead of writing a string to screen.
-    ALOGD("%s", String8(output).string());
-    ALOGD("%d", int(output.size()));
 
     return Status::ok();
 }
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 294aec8..449a2b8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -121,6 +121,11 @@
     status_t cmd_print_uid_map(FILE* out);
 
     /**
+     * Print contents of a pulled metrics source.
+     */
+    status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
+
+    /**
      * Update a configuration.
      */
     void set_config(int uid, const string& name, const StatsdConfig& config);
@@ -132,9 +137,8 @@
 
     /**
      * Fetches external metrics.
-     * TODO: This should be an sp<>
      */
-    StatsPullerManager mStatsPullerManager;
+    sp<StatsPullerManager> mStatsPullerManager;
 
     /**
      * Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.cpp b/cmds/statsd/src/external/KernelWakelockPuller.cpp
index b9abee0..ee072f8 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.cpp
+++ b/cmds/statsd/src/external/KernelWakelockPuller.cpp
@@ -37,9 +37,9 @@
 
 // The reading and parsing are implemented in Java. It is not difficult to port over. But for now
 // let StatsCompanionService handle that and send the data back.
-String16 KernelWakelockPuller::pull() {
+vector<StatsLogEventWrapper> KernelWakelockPuller::pull() {
     sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService();
-    String16 returned_value("");
+    vector<StatsLogEventWrapper> returned_value;
     if (statsCompanion != NULL) {
         Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS,
                                                  &returned_value);
@@ -47,12 +47,10 @@
             ALOGW("error pulling kernel wakelock");
         }
         ALOGD("KernelWakelockPuller::pull succeeded!");
-        // TODO: remove this when we integrate into aggregation chain.
-        ALOGD("%s", String8(returned_value).string());
         return returned_value;
     } else {
         ALOGW("statsCompanion not found!");
-        return String16();
+        return returned_value;
     }
 }
 
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.h b/cmds/statsd/src/external/KernelWakelockPuller.h
index 1ec3376..c12806c 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.h
+++ b/cmds/statsd/src/external/KernelWakelockPuller.h
@@ -29,7 +29,7 @@
     // a number of stats need to be pulled from StatsCompanionService
     //
     const static int PULL_CODE_KERNEL_WAKELOCKS;
-    String16 pull() override;
+    vector<StatsLogEventWrapper> pull() override;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 5e556b8..6655629 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -17,7 +17,12 @@
 #ifndef STATSD_STATSPULLER_H
 #define STATSD_STATSPULLER_H
 
+#include <android/os/StatsLogEventWrapper.h>
 #include <utils/String16.h>
+#include <vector>
+
+using android::os::StatsLogEventWrapper;
+using std::vector;
 
 namespace android {
 namespace os {
@@ -26,8 +31,8 @@
 class StatsPuller {
 public:
     virtual ~StatsPuller(){};
-    // use string for now, until we figure out how to integrate into the aggregation path
-    virtual String16 pull() = 0;
+
+    virtual vector<StatsLogEventWrapper> pull() = 0;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 6e8d58bc..7f554d3 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -21,6 +21,11 @@
 #include "KernelWakelockPuller.h"
 #include "StatsService.h"
 #include "external/StatsPullerManager.h"
+#include "logd/LogEvent.h"
+#include <cutils/log.h>
+#include <algorithm>
+
+#include <iostream>
 
 using namespace android;
 
@@ -35,13 +40,27 @@
             {static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()});
 }
 
-String16 StatsPullerManager::pull(int pullCode) {
+vector<std::shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode) {
     if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
+
+    vector<std::shared_ptr<LogEvent>> ret;
     if (mStatsPullers.find(pullCode) != mStatsPullers.end()) {
-        return (mStatsPullers.find(pullCode)->second)->pull();
+        vector<StatsLogEventWrapper> outputs = (mStatsPullers.find(pullCode)->second)->pull();
+        for (const StatsLogEventWrapper& it : outputs) {
+            log_msg tmp;
+            tmp.entry_v1.len = it.bytes.size();
+            // Manually set the header size to 28 bytes to match the pushed log events.
+            tmp.entry.hdr_size = 28;
+            // And set the received bytes starting after the 28 bytes reserved for header.
+            std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + 28);
+            std::shared_ptr<LogEvent> evt = std::make_shared<LogEvent>(tmp);
+            ret.push_back(evt);
+            // ret.emplace_back(tmp);
+        }
+        return ret;
     } else {
         ALOGD("Unknown pull code %d", pullCode);
-        return String16();
+        return ret;  // Return early since we don't know what to pull.
     }
 }
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index f143424..e46aec1 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -20,6 +20,8 @@
 #include <utils/String16.h>
 #include <unordered_map>
 #include "external/StatsPuller.h"
+#include "logd/LogEvent.h"
+#include "matchers/matcher_util.h"
 
 namespace android {
 namespace os {
@@ -27,7 +29,7 @@
 
 const static int KERNEL_WAKELOCKS = 1;
 
-class StatsPullerManager {
+class StatsPullerManager : public virtual RefBase {
 public:
     // Enums of pulled data types (pullCodes)
     // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
@@ -35,7 +37,8 @@
     const static int KERNEL_WAKELOCKS;
     StatsPullerManager();
 
-    String16 pull(const int pullCode);
+    // We return a vector of shared_ptr since LogEvent's copy constructor is not available.
+    vector<std::shared_ptr<LogEvent>> Pull(const int pullCode);
 
 private:
     std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 032b4b8..fb992c1 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -23,29 +23,30 @@
 namespace statsd {
 
 using std::ostringstream;
+using std::string;
 
-LogEvent::LogEvent(const log_msg& msg) {
-    init(msg);
+// 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(int64_t timestampNs, android_log_event_list* reader) {
-    init(timestampNs, reader);
+LogEvent::LogEvent(int tag) : mList(tag) {
 }
 
 LogEvent::~LogEvent() {
 }
 
+void LogEvent::init() {
+    mList.convert_to_reader();
+    init(mTimestampNs, &mList);
+}
+
 /**
  * The elements of each log event are stored as a vector of android_log_list_elements.
  * 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(const log_msg& msg) {
-
-    android_log_event_list list(const_cast<log_msg&>(msg));
-    init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &list);
-}
-
 void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
     mTimestampNs = timestampNs;
     mTagId = reader->tag();
@@ -79,6 +80,10 @@
     } 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;
@@ -109,7 +114,8 @@
         *err = BAD_TYPE;
         return NULL;
     }
-    return elem.data.string;
+    // Need to add the '/0' at the end by specifying the length of the string.
+    return string(elem.data.string, elem.len).c_str();
 }
 
 bool LogEvent::GetBool(size_t key, status_t* err) const {
@@ -189,7 +195,8 @@
         } else if (elem.type == EVENT_TYPE_FLOAT) {
             result << elem.data.float32;
         } else if (elem.type == EVENT_TYPE_STRING) {
-            result << elem.data.string;
+            // Need to add the '/0' at the end by specifying the length of the string.
+            result << string(elem.data.string, elem.len).c_str();
         }
     }
     result << " }";
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 464afca..4102675 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -22,6 +22,7 @@
 #include <log/log_event_list.h>
 #include <log/log_read.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -40,12 +41,16 @@
     /**
      * Read a LogEvent from a log_msg.
      */
-    explicit LogEvent(const log_msg& msg);
+    explicit LogEvent(log_msg msg);
 
     /**
-     * Read a LogEvent from an android_log_context.
+     * 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.
      */
-    explicit LogEvent(int64_t timestampNs, android_log_event_list* reader);
+    explicit LogEvent(int tag);
+
     ~LogEvent();
 
     /**
@@ -85,6 +90,19 @@
      */
     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.
+     */
+    void init();
+
 private:
     /**
      * Don't copy, it's slower. If we really need this we can add it but let's try to
@@ -103,6 +121,8 @@
     void init(int64_t timestampNs, android_log_event_list* reader);
 
     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;
     long mTimestampNs;
     int mTagId;
 };
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 1f07914..28cb503 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -124,52 +124,23 @@
     mCondition = conditionMet;
 }
 
-void CountMetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
+void CountMetricProducer::onMatchedLogEventInternal(
+        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const map<string, HashableDimensionKey>& conditionKey, bool condition,
+        const LogEvent& event) {
     uint64_t eventTimeNs = event.GetTimestampNs();
-    // this is old event, maybe statsd restarted?
-    if (eventTimeNs < mStartTimeNs) {
-        return;
-    }
 
     flushCounterIfNeeded(eventTimeNs);
 
-    if (mConditionSliced) {
-        map<string, HashableDimensionKey> conditionKeys;
-        for (const auto& link : mConditionLinks) {
-            VLOG("Condition link key_in_main size %d", link.key_in_main_size());
-            HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
-            conditionKeys[link.condition()] = conditionKey;
-        }
-        if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
-            VLOG("metric %lld sliced condition not met", mMetric.metric_id());
-            return;
-        }
-    } else {
-        if (!mCondition) {
-            VLOG("metric %lld condition not met", mMetric.metric_id());
-            return;
-        }
+    if (condition == false) {
+        return;
     }
 
-    HashableDimensionKey hashableKey;
-
-    if (mDimension.size() > 0) {
-        vector<KeyValuePair> key = getDimensionKey(event, mDimension);
-        hashableKey = getHashableKey(key);
-        // Add the HashableDimensionKey->vector<KeyValuePair> to the map, because StatsLogReport
-        // expects vector<KeyValuePair>.
-        if (mDimensionKeyMap.find(hashableKey) == mDimensionKeyMap.end()) {
-            mDimensionKeyMap[hashableKey] = key;
-        }
-    } else {
-        hashableKey = DEFAULT_DIMENSION_KEY;
-    }
-
-    auto it = mCurrentSlicedCounter.find(hashableKey);
+    auto it = mCurrentSlicedCounter.find(eventKey);
 
     if (it == mCurrentSlicedCounter.end()) {
         // create a counter for the new key
-        mCurrentSlicedCounter[hashableKey] = 1;
+        mCurrentSlicedCounter[eventKey] = 1;
 
     } else {
         // increment the existing value
@@ -177,8 +148,8 @@
         count++;
     }
 
-    VLOG("metric %lld %s->%d", mMetric.metric_id(), hashableKey.c_str(),
-         mCurrentSlicedCounter[hashableKey]);
+    VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
+         mCurrentSlicedCounter[eventKey]);
 }
 
 // When a new matched event comes in, we check if event falls into the current
@@ -215,4 +186,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index f0d6025..8bbdb01 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -41,8 +41,6 @@
 
     virtual ~CountMetricProducer();
 
-    void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) override;
-
     void onConditionChanged(const bool conditionMet) override;
 
     void finish() override;
@@ -54,6 +52,11 @@
     // TODO: Implement this later.
     virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
 
+protected:
+    void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+                                   const std::map<std::string, HashableDimensionKey>& conditionKey,
+                                   bool condition, const LogEvent& event) override;
+
 private:
     const CountMetric mMetric;
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index aa597f4..38e55fd 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -137,11 +137,10 @@
     return report;
 };
 
-void DurationMetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
-    if (event.GetTimestampNs() < mStartTimeNs) {
-        return;
-    }
-
+void DurationMetricProducer::onMatchedLogEventInternal(
+        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const map<string, HashableDimensionKey>& conditionKeys, bool condition,
+        const LogEvent& event) {
     flushDurationIfNeeded(event.GetTimestampNs());
 
     if (matcherIndex == mStopAllIndex) {
@@ -149,49 +148,20 @@
         return;
     }
 
-    HashableDimensionKey hashableKey;
-    if (mDimension.size() > 0) {
-        // hook up sliced counter with AnomalyMonitor.
-        vector<KeyValuePair> key = getDimensionKey(event, mDimension);
-        hashableKey = getHashableKey(key);
-        // Add the HashableDimensionKey->DimensionKey to the map, because StatsLogReport expects
-        // vector<KeyValuePair>.
-        if (mDimensionKeyMap.find(hashableKey) == mDimensionKeyMap.end()) {
-            mDimensionKeyMap[hashableKey] = key;
-        }
-    } else {
-        hashableKey = DEFAULT_DIMENSION_KEY;
-    }
-
-    if (mCurrentSlicedDuration.find(hashableKey) == mCurrentSlicedDuration.end() &&
-        mConditionSliced) {
+    if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end() && mConditionSliced) {
         // add the durationInfo for the current bucket.
-        auto& durationInfo = mCurrentSlicedDuration[hashableKey];
-        auto& conditionKeys = durationInfo.conditionKeys;
-        // get and cache the keys for query condition.
-        for (const auto& link : mConditionLinks) {
-            HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
-            conditionKeys[link.condition()] = conditionKey;
-        }
-    }
-
-    bool conditionMet;
-    if (mConditionSliced) {
-        const auto& conditionKeys = mCurrentSlicedDuration[hashableKey].conditionKeys;
-        conditionMet =
-                mWizard->query(mConditionTrackerIndex, conditionKeys) == ConditionState::kTrue;
-    } else {
-        conditionMet = mCondition;
+        auto& durationInfo = mCurrentSlicedDuration[eventKey];
+        durationInfo.conditionKeys = conditionKeys;
     }
 
     if (matcherIndex == mStartIndex) {
-        VLOG("Metric %lld Key: %s Start, Condition %d", mMetric.metric_id(), hashableKey.c_str(),
-             conditionMet);
-        noteStart(hashableKey, conditionMet, event.GetTimestampNs());
+        VLOG("Metric %lld Key: %s Start, Condition %d", mMetric.metric_id(), eventKey.c_str(),
+             condition);
+        noteStart(eventKey, condition, event.GetTimestampNs());
     } else if (matcherIndex == mStopIndex) {
-        VLOG("Metric %lld Key: %s Stop, Condition %d", mMetric.metric_id(), hashableKey.c_str(),
-             conditionMet);
-        noteStop(hashableKey, event.GetTimestampNs());
+        VLOG("Metric %lld Key: %s Stop, Condition %d", mMetric.metric_id(), eventKey.c_str(),
+             condition);
+        noteStop(eventKey, event.GetTimestampNs());
     }
 }
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 44c3254..19e2437 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -61,8 +61,6 @@
 
     virtual ~DurationMetricProducer();
 
-    void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) override;
-
     void onConditionChanged(const bool conditionMet) override;
 
     void finish() override;
@@ -74,6 +72,11 @@
     // TODO: Implement this later.
     virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
 
+protected:
+    void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+                                   const std::map<std::string, HashableDimensionKey>& conditionKeys,
+                                   bool condition, const LogEvent& event) override;
+
 private:
     const DurationMetric mMetric;
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
new file mode 100644
index 0000000..3c8ce6e
--- /dev/null
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "MetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+
+void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
+    uint64_t eventTimeNs = event.GetTimestampNs();
+    // this is old event, maybe statsd restarted?
+    if (eventTimeNs < mStartTimeNs) {
+        return;
+    }
+
+    HashableDimensionKey eventKey;
+
+    if (mDimension.size() > 0) {
+        vector<KeyValuePair> key = getDimensionKey(event, mDimension);
+        eventKey = getHashableKey(key);
+        // Add the HashableDimensionKey->vector<KeyValuePair> to the map, because StatsLogReport
+        // expects vector<KeyValuePair>.
+        if (mDimensionKeyMap.find(eventKey) == mDimensionKeyMap.end()) {
+            mDimensionKeyMap[eventKey] = key;
+        }
+    } else {
+        eventKey = DEFAULT_DIMENSION_KEY;
+    }
+
+    bool condition;
+
+    map<string, HashableDimensionKey> conditionKeys;
+    if (mConditionSliced) {
+        for (const auto& link : mConditionLinks) {
+            HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
+            conditionKeys[link.condition()] = conditionKey;
+        }
+        if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
+            condition = false;
+        } else {
+            condition = true;
+        }
+    } else {
+        condition = mCondition;
+    }
+
+    onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index afaab648..496b145 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -48,7 +48,7 @@
     virtual ~MetricProducer(){};
 
     // Consume the parsed stats log entry that already matched the "what" of the metric.
-    virtual void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) = 0;
+    void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event);
 
     virtual void onConditionChanged(const bool condition) = 0;
 
@@ -86,6 +86,26 @@
     std::unordered_map<HashableDimensionKey, std::vector<KeyValuePair>> mDimensionKeyMap;
 
     std::vector<EventConditionLink> mConditionLinks;
+
+    /*
+     * Individual metrics can implement their own business logic here. All pre-processing is done.
+     *
+     * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
+     *                 DurationMetric, because it has start/stop/stop_all 3 matchers.
+     * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
+     *             dimensions, it will be DEFAULT_DIMENSION_KEY
+     * [conditionKey]: the keys of conditions which should be used to query the condition for this
+     *                 target event (from EventConditionLink). This is passed to individual metrics
+     *                 because DurationMetric needs it to be cached.
+     * [condition]: whether condition is met. If condition is sliced, this is the result coming from
+     *              query with ConditionWizard; If condition is not sliced, this is the
+     *              nonSlicedCondition.
+     * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
+     */
+    virtual void onMatchedLogEventInternal(
+            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+            const LogEvent& event) = 0;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 19403c0..fdfe8ef 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -42,12 +42,10 @@
     auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
     simpleMatcher->set_tag(TAG_ID);
 
-    // Set up the event
-    android_log_event_list list(TAG_ID);
+    LogEvent event(TAG_ID);
 
     // Convert to a LogEvent
-    list.convert_to_reader();
-    LogEvent event(999, &list);
+    event.init();
 
     // Test
     EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
@@ -64,13 +62,13 @@
     keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
 
     // Set up the event
-    android_log_event_list list(TAG_ID);
-    list << true;
-    list << false;
+    LogEvent event(TAG_ID);
+    auto list = event.GetAndroidLogEventList();
+    *list << true;
+    *list << false;
 
     // Convert to a LogEvent
-    list.convert_to_reader();
-    LogEvent event(999, &list);
+    event.init();
 
     // Test
     keyValue1->set_eq_bool(true);
@@ -100,12 +98,12 @@
     keyValue->set_eq_string("some value");
 
     // Set up the event
-    android_log_event_list list(TAG_ID);
-    list << "some value";
+    LogEvent event(TAG_ID);
+    auto list = event.GetAndroidLogEventList();
+    *list << "some value";
 
     // Convert to a LogEvent
-    list.convert_to_reader();
-    LogEvent event(999, &list);
+    event.init();
 
     // Test
     EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
@@ -121,12 +119,11 @@
     keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
 
     // Set up the event
-    android_log_event_list list(TAG_ID);
-    list << 11;
+    LogEvent event(TAG_ID);
+    auto list = event.GetAndroidLogEventList();
+    *list << 11;
 
-    // Convert to a LogEvent
-    list.convert_to_reader();
-    LogEvent event(999, &list);
+    event.init();
 
     // Test
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3c53063..ab8edee 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6533,6 +6533,52 @@
     }
 
     /**
+     * Called by device owner to set the system wall clock time. This only takes effect if called
+     * when {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be
+     * returned.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+     * @param millis time in milliseconds since the Epoch
+     * @return {@code true} if set time succeeded, {@code false} otherwise.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public boolean setTime(@NonNull ComponentName admin, long millis) {
+        throwIfParentInstance("setTime");
+        if (mService != null) {
+            try {
+                return mService.setTime(admin, millis);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called by device owner to set the system's persistent default time zone. This only takes
+     * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE} is 0, otherwise
+     * {@code false} will be returned.
+     *
+     * @see android.app.AlarmManager#setTimeZone(String)
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+     * @param timeZone one of the Olson ids from the list returned by
+     *     {@link java.util.TimeZone#getAvailableIDs}
+     * @return {@code true} if set timezone succeeded, {@code false} otherwise.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public boolean setTimeZone(@NonNull ComponentName admin, String timeZone) {
+        throwIfParentInstance("setTimeZone");
+        if (mService != null) {
+            try {
+                return mService.setTimeZone(admin, timeZone);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Called by profile or device owners to update {@link android.provider.Settings.Secure}
      * settings. Validation that the value of the setting is in the correct form for the setting
      * type should be performed by the caller.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8865a05..e77c186 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -229,6 +229,9 @@
     void setGlobalSetting(in ComponentName who, in String setting, in String value);
     void setSecureSetting(in ComponentName who, in String setting, in String value);
 
+    boolean setTime(in ComponentName who, long millis);
+    boolean setTimeZone(in ComponentName who, String timeZone);
+
     void setMasterVolumeMuted(in ComponentName admin, boolean on);
     boolean isMasterVolumeMuted(in ComponentName admin);
 
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 969b19e..37bb6b0 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -20,17 +20,19 @@
 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.app.IServiceConnection;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.ServiceConnection;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
-import android.os.IBinder;
+import android.os.Handler;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -1051,43 +1053,23 @@
      * The appWidgetId specified must already be bound to the calling AppWidgetHost via
      * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
      *
-     * @param packageName   The package from which the binding is requested.
      * @param appWidgetId   The AppWidget instance for which to bind the RemoteViewsService.
      * @param intent        The intent of the service which will be providing the data to the
      *                      RemoteViewsAdapter.
      * @param connection    The callback interface to be notified when a connection is made or lost.
+     * @param flags         Flags used for binding to the service
+     *
+     * @see Context#getServiceDispatcher(ServiceConnection, Handler, int)
      * @hide
      */
-    public void bindRemoteViewsService(String packageName, int appWidgetId, Intent intent,
-            IBinder connection) {
+    public boolean bindRemoteViewsService(Context context, int appWidgetId, Intent intent,
+            IServiceConnection connection, @Context.BindServiceFlags int flags) {
         if (mService == null) {
-            return;
+            return false;
         }
         try {
-            mService.bindRemoteViewsService(packageName, appWidgetId, intent, connection);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Unbinds the RemoteViewsService for a given appWidgetId and intent.
-     *
-     * The appWidgetId specified muse already be bound to the calling AppWidgetHost via
-     * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
-     *
-     * @param packageName   The package from which the binding is requested.
-     * @param appWidgetId   The AppWidget instance for which to bind the RemoteViewsService.
-     * @param intent        The intent of the service which will be providing the data to the
-     *                      RemoteViewsAdapter.
-     * @hide
-     */
-    public void unbindRemoteViewsService(String packageName, int appWidgetId, Intent intent) {
-        if (mService == null) {
-            return;
-        }
-        try {
-            mService.unbindRemoteViewsService(packageName, appWidgetId, intent);
+            return mService.bindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent,
+                    context.getIApplicationThread(), context.getActivityToken(), connection, flags);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 20fbf04..c165fb3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3604,7 +3604,6 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
-     * {@link android.text.ClipboardManager} for accessing and modifying
      * {@link android.content.ClipboardManager} for accessing and modifying
      * the contents of the global clipboard.
      *
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5995696..8682c01 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -447,8 +447,7 @@
 
         /**
          * Returns the max duration if it is being tracked.
-         * Not all Timer subclasses track the max, total, current durations.
-
+         * Not all Timer subclasses track the max, total, and current durations.
          */
         public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
             return -1;
@@ -456,14 +455,14 @@
 
         /**
          * Returns the current time the timer has been active, if it is being tracked.
-         * Not all Timer subclasses track the max, total, current durations.
+         * Not all Timer subclasses track the max, total, and current durations.
          */
         public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
             return -1;
         }
 
         /**
-         * Returns the current time the timer has been active, if it is being tracked.
+         * Returns the total time the timer has been active, if it is being tracked.
          *
          * Returns the total cumulative duration (i.e. sum of past durations) that this timer has
          * been on since reset.
@@ -471,7 +470,7 @@
          * depending on the Timer, getTotalTimeLocked may represent the total 'blamed' or 'pooled'
          * time, rather than the actual time. By contrast, getTotalDurationMsLocked always gives
          * the actual total time.
-         * Not all Timer subclasses track the max, total, current durations.
+         * Not all Timer subclasses track the max, total, and current durations.
          */
         public long getTotalDurationMsLocked(long elapsedRealtimeMs) {
             return -1;
@@ -600,9 +599,17 @@
         public abstract long getFullWifiLockTime(long elapsedRealtimeUs, int which);
         public abstract long getWifiScanTime(long elapsedRealtimeUs, int which);
         public abstract int getWifiScanCount(int which);
+        /**
+         * Returns the timer keeping track of wifi scans.
+         */
+        public abstract Timer getWifiScanTimer();
         public abstract int getWifiScanBackgroundCount(int which);
         public abstract long getWifiScanActualTime(long elapsedRealtimeUs);
         public abstract long getWifiScanBackgroundTime(long elapsedRealtimeUs);
+        /**
+         * Returns the timer keeping track of background wifi scans.
+         */
+        public abstract Timer getWifiScanBackgroundTimer();
         public abstract long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which);
         public abstract int getWifiBatchedScanCount(int csphBin, int which);
         public abstract long getWifiMulticastTime(long elapsedRealtimeUs, int which);
@@ -2824,6 +2831,10 @@
         }
     }
 
+    private static long roundUsToMs(long timeUs) {
+        return (timeUs + 500) / 1000;
+    }
+
     private static long computeWakeLock(Timer timer, long elapsedRealtimeUs, int which) {
         if (timer != null) {
             // Convert from microseconds to milliseconds with rounding
@@ -3028,8 +3039,7 @@
                                         Timer timer, long rawRealtime, int which) {
         if (timer != null) {
             // Convert from microseconds to milliseconds with rounding
-            final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
-                    / 1000;
+            final long totalTime = roundUsToMs(timer.getTotalTimeLocked(rawRealtime, which));
             final int count = timer.getCountLocked(which);
             if (totalTime != 0 || count != 0) {
                 dumpLine(pw, uid, category, type, totalTime, count);
@@ -3052,12 +3062,26 @@
             return;
         }
         // Convert from microseconds to milliseconds with rounding
-        final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtimeUs, which) + 500) / 1000;
+        final long timeMs = roundUsToMs(timer.getTotalTimeLocked(rawRealtimeUs, which));
         final int count = timer.getCountLocked(which);
-        if (totalTimeMs != 0 || count != 0) {
+        final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs / 1000);
+        final long curDurationMs = timer.getCurrentDurationMsLocked(rawRealtimeUs / 1000);
+        final long totalDurationMs = timer.getTotalDurationMsLocked(rawRealtimeUs / 1000);
+        if (timeMs != 0 || count != 0 || maxDurationMs != -1 || curDurationMs != -1
+                || totalDurationMs != -1) {
             final long token = proto.start(fieldId);
-            proto.write(TimerProto.DURATION_MS, totalTimeMs);
+            proto.write(TimerProto.DURATION_MS, timeMs);
             proto.write(TimerProto.COUNT, count);
+            // These values will be -1 for timers that don't implement the functionality.
+            if (maxDurationMs != -1) {
+                proto.write(TimerProto.MAX_DURATION_MS, maxDurationMs);
+            }
+            if (curDurationMs != -1) {
+                proto.write(TimerProto.CURRENT_DURATION_MS, curDurationMs);
+            }
+            if (totalDurationMs != -1) {
+                proto.write(TimerProto.TOTAL_DURATION_MS, totalDurationMs);
+            }
             proto.end(token);
         }
     }
@@ -3770,7 +3794,7 @@
                 linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW),
                         rawRealtime, "w", which, linePrefix);
 
-                // Only log if we had at lease one wakelock...
+                // Only log if we had at least one wakelock...
                 if (sb.length() > 0) {
                     String name = wakelocks.keyAt(iw);
                     if (name.indexOf(',') >= 0) {
@@ -6126,9 +6150,8 @@
             return;
         }
         int count = steps.mNumStepDurations;
-        long token;
         for (int i = 0; i < count; ++i) {
-            token = proto.start(fieldId);
+            long token = proto.start(fieldId);
             proto.write(SystemProto.BatteryLevelStep.DURATION_MS, steps.getDurationAt(i));
             proto.write(SystemProto.BatteryLevelStep.LEVEL, steps.getLevelAt(i));
 
@@ -6621,15 +6644,443 @@
         }
 
         if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
-            // TODO: implement dumpProtoAppsLocked(proto, apps);
-            dumpProtoSystemLocked(context, proto, (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
+            final BatteryStatsHelper helper = new BatteryStatsHelper(context, false,
+                    (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
+            helper.create(this);
+            helper.refreshStats(STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+
+            dumpProtoAppsLocked(proto, helper, apps);
+            dumpProtoSystemLocked(proto, helper);
         }
 
         proto.end(bToken);
         proto.flush();
     }
 
-    private void dumpProtoSystemLocked(Context context, ProtoOutputStream proto, boolean wifiOnly) {
+    private void dumpProtoAppsLocked(ProtoOutputStream proto, BatteryStatsHelper helper,
+            List<ApplicationInfo> apps) {
+        final int which = STATS_SINCE_CHARGED;
+        final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
+        final long rawRealtimeMs = SystemClock.elapsedRealtime();
+        final long rawRealtimeUs = rawRealtimeMs * 1000;
+        final long batteryUptimeUs = getBatteryUptime(rawUptimeUs);
+
+        SparseArray<ArrayList<String>> aidToPackages = new SparseArray<>();
+        if (apps != null) {
+            for (int i = 0; i < apps.size(); ++i) {
+                ApplicationInfo ai = apps.get(i);
+                int aid = UserHandle.getAppId(ai.uid);
+                ArrayList<String> pkgs = aidToPackages.get(aid);
+                if (pkgs == null) {
+                    pkgs = new ArrayList<String>();
+                    aidToPackages.put(aid, pkgs);
+                }
+                pkgs.add(ai.packageName);
+            }
+        }
+
+        SparseArray<BatterySipper> uidToSipper = new SparseArray<>();
+        final List<BatterySipper> sippers = helper.getUsageList();
+        if (sippers != null) {
+            for (int i = 0; i < sippers.size(); ++i) {
+                final BatterySipper bs = sippers.get(i);
+                if (bs.drainType != BatterySipper.DrainType.APP) {
+                    // Others are handled by dumpProtoSystemLocked()
+                    continue;
+                }
+                uidToSipper.put(bs.uidObj.getUid(), bs);
+            }
+        }
+
+        SparseArray<? extends Uid> uidStats = getUidStats();
+        final int n = uidStats.size();
+        for (int iu = 0; iu < n; ++iu) {
+            final long uTkn = proto.start(BatteryStatsProto.UIDS);
+            final Uid u = uidStats.valueAt(iu);
+
+            final int uid = uidStats.keyAt(iu);
+            proto.write(UidProto.UID, uid);
+
+            // Print packages and apk stats (UID_DATA & APK_DATA)
+            ArrayList<String> pkgs = aidToPackages.get(UserHandle.getAppId(uid));
+            if (pkgs == null) {
+                pkgs = new ArrayList<String>();
+            }
+            final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats =
+                    u.getPackageStats();
+            for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
+                String pkg = packageStats.keyAt(ipkg);
+                final ArrayMap<String, ? extends  Uid.Pkg.Serv> serviceStats =
+                        packageStats.valueAt(ipkg).getServiceStats();
+                if (serviceStats.size() == 0) {
+                    // Due to the way ActivityManagerService logs wakeup alarms, some packages (for
+                    // example, "android") may be included in the packageStats that aren't part of
+                    // the UID. If they don't have any services, then they shouldn't be listed here.
+                    // These packages won't be a part in the pkgs List.
+                    continue;
+                }
+
+                final long pToken = proto.start(UidProto.PACKAGES);
+                proto.write(UidProto.Package.NAME, pkg);
+                // Remove from the packages list since we're logging it here.
+                pkgs.remove(pkg);
+
+                for (int isvc = serviceStats.size() - 1; isvc >= 0; --isvc) {
+                    final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+                    long sToken = proto.start(UidProto.Package.SERVICES);
+
+                    proto.write(UidProto.Package.Service.NAME, serviceStats.keyAt(isvc));
+                    proto.write(UidProto.Package.Service.START_DURATION_MS,
+                            roundUsToMs(ss.getStartTime(batteryUptimeUs, which)));
+                    proto.write(UidProto.Package.Service.START_COUNT, ss.getStarts(which));
+                    proto.write(UidProto.Package.Service.LAUNCH_COUNT, ss.getLaunches(which));
+
+                    proto.end(sToken);
+                }
+                proto.end(pToken);
+            }
+            // Print any remaining packages that weren't in the packageStats map. pkgs is pulled
+            // from PackageManager data. Packages are only included in packageStats if there was
+            // specific data tracked for them (services and wakeup alarms, etc.).
+            for (String p : pkgs) {
+                final long pToken = proto.start(UidProto.PACKAGES);
+                proto.write(UidProto.Package.NAME, p);
+                proto.end(pToken);
+            }
+
+            // Total wakelock data (AGGREGATED_WAKELOCK_DATA)
+            if (u.getAggregatedPartialWakelockTimer() != null) {
+                final Timer timer = u.getAggregatedPartialWakelockTimer();
+                // Times are since reset (regardless of 'which')
+                final long totTimeMs = timer.getTotalDurationMsLocked(rawRealtimeMs);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTimeMs = bgTimer != null
+                        ? bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                final long awToken = proto.start(UidProto.AGGREGATED_WAKELOCK);
+                proto.write(UidProto.AggregatedWakelock.PARTIAL_DURATION_MS, totTimeMs);
+                proto.write(UidProto.AggregatedWakelock.BACKGROUND_PARTIAL_DURATION_MS, bgTimeMs);
+                proto.end(awToken);
+            }
+
+            // Audio (AUDIO_DATA)
+            dumpTimer(proto, UidProto.AUDIO, u.getAudioTurnedOnTimer(), rawRealtimeUs, which);
+
+            // Bluetooth Controller (BLUETOOTH_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.BLUETOOTH_CONTROLLER,
+                    u.getBluetoothControllerActivity(), which);
+
+            // BLE scans (BLUETOOTH_MISC_DATA) (uses totalDurationMsLocked and MaxDurationMsLocked)
+            final Timer bleTimer = u.getBluetoothScanTimer();
+            if (bleTimer != null) {
+                final long bmToken = proto.start(UidProto.BLUETOOTH_MISC);
+
+                dumpTimer(proto, UidProto.BluetoothMisc.APPORTIONED_BLE_SCAN, bleTimer,
+                        rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.BluetoothMisc.BACKGROUND_BLE_SCAN,
+                        u.getBluetoothScanBackgroundTimer(), rawRealtimeUs, which);
+                // Unoptimized scan timer. Unpooled and since reset (regardless of 'which').
+                dumpTimer(proto, UidProto.BluetoothMisc.UNOPTIMIZED_BLE_SCAN,
+                        u.getBluetoothUnoptimizedScanTimer(), rawRealtimeUs, which);
+                // Unoptimized bg scan timer. Unpooled and since reset (regardless of 'which').
+                dumpTimer(proto, UidProto.BluetoothMisc.BACKGROUND_UNOPTIMIZED_BLE_SCAN,
+                        u.getBluetoothUnoptimizedScanBackgroundTimer(), rawRealtimeUs, which);
+                // Result counters
+                proto.write(UidProto.BluetoothMisc.BLE_SCAN_RESULT_COUNT,
+                        u.getBluetoothScanResultCounter() != null
+                            ? u.getBluetoothScanResultCounter().getCountLocked(which) : 0);
+                proto.write(UidProto.BluetoothMisc.BACKGROUND_BLE_SCAN_RESULT_COUNT,
+                        u.getBluetoothScanResultBgCounter() != null
+                            ? u.getBluetoothScanResultBgCounter().getCountLocked(which) : 0);
+
+                proto.end(bmToken);
+            }
+
+            // Camera (CAMERA_DATA)
+            dumpTimer(proto, UidProto.CAMERA, u.getCameraTurnedOnTimer(), rawRealtimeUs, which);
+
+            // CPU stats (CPU_DATA & CPU_TIMES_AT_FREQ_DATA)
+            final long cpuToken = proto.start(UidProto.CPU);
+            proto.write(UidProto.Cpu.USER_DURATION_MS, roundUsToMs(u.getUserCpuTimeUs(which)));
+            proto.write(UidProto.Cpu.SYSTEM_DURATION_MS, roundUsToMs(u.getSystemCpuTimeUs(which)));
+
+            final long[] cpuFreqs = getCpuFreqs();
+            if (cpuFreqs != null) {
+                final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
+                // If total cpuFreqTimes is null, then we don't need to check for
+                // screenOffCpuFreqTimes.
+                if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
+                    long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
+                    if (screenOffCpuFreqTimeMs == null) {
+                        screenOffCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
+                    }
+                    for (int ic = 0; ic < cpuFreqTimeMs.length; ++ic) {
+                        long cToken = proto.start(UidProto.Cpu.BY_FREQUENCY);
+                        proto.write(UidProto.Cpu.ByFrequency.FREQUENCY_INDEX, ic + 1);
+                        proto.write(UidProto.Cpu.ByFrequency.TOTAL_DURATION_MS,
+                                cpuFreqTimeMs[ic]);
+                        proto.write(UidProto.Cpu.ByFrequency.SCREEN_OFF_DURATION_MS,
+                                screenOffCpuFreqTimeMs[ic]);
+                        proto.end(cToken);
+                    }
+                }
+            }
+            proto.end(cpuToken);
+
+            // Flashlight (FLASHLIGHT_DATA)
+            dumpTimer(proto, UidProto.FLASHLIGHT, u.getFlashlightTurnedOnTimer(),
+                    rawRealtimeUs, which);
+
+            // Foreground activity (FOREGROUND_ACTIVITY_DATA)
+            dumpTimer(proto, UidProto.FOREGROUND_ACTIVITY, u.getForegroundActivityTimer(),
+                    rawRealtimeUs, which);
+
+            // Foreground service (FOREGROUND_SERVICE_DATA)
+            dumpTimer(proto, UidProto.FOREGROUND_SERVICE, u.getForegroundServiceTimer(),
+                    rawRealtimeUs, which);
+
+            // Job completion (JOB_COMPLETION_DATA)
+            final ArrayMap<String, SparseIntArray> completions = u.getJobCompletionStats();
+            final int[] reasons = new int[]{
+                JobParameters.REASON_CANCELED,
+                JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
+                JobParameters.REASON_PREEMPT,
+                JobParameters.REASON_TIMEOUT,
+                JobParameters.REASON_DEVICE_IDLE,
+            };
+            for (int ic = 0; ic < completions.size(); ++ic) {
+                SparseIntArray types = completions.valueAt(ic);
+                if (types != null) {
+                    final long jcToken = proto.start(UidProto.JOB_COMPLETION);
+
+                    proto.write(UidProto.JobCompletion.NAME, completions.keyAt(ic));
+
+                    for (int r : reasons) {
+                        long rToken = proto.start(UidProto.JobCompletion.REASON_COUNT);
+                        proto.write(UidProto.JobCompletion.ReasonCount.NAME, r);
+                        proto.write(UidProto.JobCompletion.ReasonCount.COUNT, types.get(r, 0));
+                        proto.end(rToken);
+                    }
+
+                    proto.end(jcToken);
+                }
+            }
+
+            // Scheduled jobs (JOB_DATA)
+            final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+            for (int ij = jobs.size() - 1; ij >= 0; --ij) {
+                final Timer timer = jobs.valueAt(ij);
+                final Timer bgTimer = timer.getSubTimer();
+                final long jToken = proto.start(UidProto.JOBS);
+
+                proto.write(UidProto.Job.NAME, jobs.keyAt(ij));
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Job.TOTAL, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Job.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(jToken);
+            }
+
+            // Modem Controller (MODEM_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.MODEM_CONTROLLER,
+                    u.getModemControllerActivity(), which);
+
+            // Network stats (NETWORK_DATA)
+            final long nToken = proto.start(UidProto.NETWORK);
+            proto.write(UidProto.Network.MOBILE_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+            proto.write(UidProto.Network.BT_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
+            proto.write(UidProto.Network.BT_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_RX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_TX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_RX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_TX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_ACTIVE_DURATION_MS,
+                    roundUsToMs(u.getMobileRadioActiveTime(which)));
+            proto.write(UidProto.Network.MOBILE_ACTIVE_COUNT,
+                    u.getMobileRadioActiveCount(which));
+            proto.write(UidProto.Network.MOBILE_WAKEUP_COUNT,
+                    u.getMobileRadioApWakeupCount(which));
+            proto.write(UidProto.Network.WIFI_WAKEUP_COUNT,
+                    u.getWifiRadioApWakeupCount(which));
+            proto.write(UidProto.Network.MOBILE_BYTES_BG_RX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_BG_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_BYTES_BG_TX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_BG_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_BG_RX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_BG_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_BG_TX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_BG_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_BG_RX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_BG_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_BG_TX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_BG_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_BG_RX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_BG_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_BG_TX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_BG_TX_DATA, which));
+            proto.end(nToken);
+
+            // Power use item (POWER_USE_ITEM_DATA)
+            BatterySipper bs = uidToSipper.get(uid);
+            if (bs != null) {
+                final long bsToken = proto.start(UidProto.POWER_USE_ITEM);
+                proto.write(UidProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
+                proto.write(UidProto.PowerUseItem.SHOULD_HIDE, bs.shouldHide);
+                proto.write(UidProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
+                proto.write(UidProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
+                        bs.proportionalSmearMah);
+                proto.end(bsToken);
+            }
+
+            // Processes (PROCESS_DATA)
+            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats =
+                    u.getProcessStats();
+            for (int ipr = processStats.size() - 1; ipr >= 0; --ipr) {
+                final Uid.Proc ps = processStats.valueAt(ipr);
+                final long prToken = proto.start(UidProto.PROCESS);
+
+                proto.write(UidProto.Process.NAME, processStats.keyAt(ipr));
+                proto.write(UidProto.Process.USER_DURATION_MS, ps.getUserTime(which));
+                proto.write(UidProto.Process.SYSTEM_DURATION_MS, ps.getSystemTime(which));
+                proto.write(UidProto.Process.FOREGROUND_DURATION_MS, ps.getForegroundTime(which));
+                proto.write(UidProto.Process.START_COUNT, ps.getStarts(which));
+                proto.write(UidProto.Process.ANR_COUNT, ps.getNumAnrs(which));
+                proto.write(UidProto.Process.CRASH_COUNT, ps.getNumCrashes(which));
+
+                proto.end(prToken);
+            }
+
+            // Sensors (SENSOR_DATA)
+            final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+            for (int ise = 0; ise < sensors.size(); ++ise) {
+                final Uid.Sensor se = sensors.valueAt(ise);
+                final Timer timer = se.getSensorTime();
+                if (timer == null) {
+                    continue;
+                }
+                final Timer bgTimer = se.getSensorBackgroundTime();
+                final int sensorNumber = sensors.keyAt(ise);
+                final long seToken = proto.start(UidProto.SENSORS);
+
+                proto.write(UidProto.Sensor.ID, sensorNumber);
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Sensor.APPORTIONED, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Sensor.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(seToken);
+            }
+
+            // State times (STATE_TIME_DATA)
+            for (int ips = 0; ips < Uid.NUM_PROCESS_STATE; ++ips) {
+                long durMs = roundUsToMs(u.getProcessStateTime(ips, rawRealtimeUs, which));
+                if (durMs == 0) {
+                    continue;
+                }
+                final long stToken = proto.start(UidProto.STATES);
+                proto.write(UidProto.StateTime.STATE, ips);
+                proto.write(UidProto.StateTime.DURATION_MS, durMs);
+                proto.end(stToken);
+            }
+
+            // Syncs (SYNC_DATA)
+            final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+            for (int isy = syncs.size() - 1; isy >= 0; --isy) {
+                final Timer timer = syncs.valueAt(isy);
+                final Timer bgTimer = timer.getSubTimer();
+                final long syToken = proto.start(UidProto.SYNCS);
+
+                proto.write(UidProto.Sync.NAME, syncs.keyAt(isy));
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Sync.TOTAL, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Sync.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(syToken);
+            }
+
+            // User activity (USER_ACTIVITY_DATA)
+            if (u.hasUserActivity()) {
+                for (int i = 0; i < Uid.NUM_USER_ACTIVITY_TYPES; ++i) {
+                    int val = u.getUserActivityCount(i, which);
+                    if (val != 0) {
+                        final long uaToken = proto.start(UidProto.USER_ACTIVITY);
+                        proto.write(UidProto.UserActivity.NAME, i);
+                        proto.write(UidProto.UserActivity.COUNT, val);
+                        proto.end(uaToken);
+                    }
+                }
+            }
+
+            // Vibrator (VIBRATOR_DATA)
+            dumpTimer(proto, UidProto.VIBRATOR, u.getVibratorOnTimer(), rawRealtimeUs, which);
+
+            // Video (VIDEO_DATA)
+            dumpTimer(proto, UidProto.VIDEO, u.getVideoTurnedOnTimer(), rawRealtimeUs, which);
+
+            // Wakelocks (WAKELOCK_DATA)
+            final ArrayMap<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
+            for (int iw = wakelocks.size() - 1; iw >= 0; --iw) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+                final long wToken = proto.start(UidProto.WAKELOCKS);
+                proto.write(UidProto.Wakelock.NAME, wakelocks.keyAt(iw));
+                dumpTimer(proto, UidProto.Wakelock.FULL, wl.getWakeTime(WAKE_TYPE_FULL),
+                        rawRealtimeUs, which);
+                final Timer pTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (pTimer != null) {
+                    dumpTimer(proto, UidProto.Wakelock.PARTIAL, pTimer, rawRealtimeUs, which);
+                    dumpTimer(proto, UidProto.Wakelock.BACKGROUND_PARTIAL, pTimer.getSubTimer(),
+                            rawRealtimeUs, which);
+                }
+                dumpTimer(proto, UidProto.Wakelock.WINDOW, wl.getWakeTime(WAKE_TYPE_WINDOW),
+                        rawRealtimeUs, which);
+                proto.end(wToken);
+            }
+
+            // Wakeup alarms (WAKEUP_ALARM_DATA)
+            for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
+                final Uid.Pkg ps = packageStats.valueAt(ipkg);
+                final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+                for (int iwa = alarms.size() - 1; iwa >= 0; --iwa) {
+                    final long waToken = proto.start(UidProto.WAKEUP_ALARM);
+                    proto.write(UidProto.WakeupAlarm.NAME, alarms.keyAt(iwa));
+                    proto.write(UidProto.WakeupAlarm.COUNT,
+                            alarms.valueAt(iwa).getCountLocked(which));
+                    proto.end(waToken);
+                }
+            }
+
+            // Wifi Controller (WIFI_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.WIFI_CONTROLLER,
+                    u.getWifiControllerActivity(), which);
+
+            // Wifi data (WIFI_DATA)
+            final long wToken = proto.start(UidProto.WIFI);
+            proto.write(UidProto.Wifi.FULL_WIFI_LOCK_DURATION_MS,
+                    roundUsToMs(u.getFullWifiLockTime(rawRealtimeUs, which)));
+            dumpTimer(proto, UidProto.Wifi.APPORTIONED_SCAN, u.getWifiScanTimer(),
+                    rawRealtimeUs, which);
+            proto.write(UidProto.Wifi.RUNNING_DURATION_MS,
+                    roundUsToMs(u.getWifiRunningTime(rawRealtimeUs, which)));
+            dumpTimer(proto, UidProto.Wifi.BACKGROUND_SCAN, u.getWifiScanBackgroundTimer(),
+                    rawRealtimeUs, which);
+            proto.end(wToken);
+
+            proto.end(uTkn);
+        }
+    }
+
+    private void dumpProtoSystemLocked(ProtoOutputStream proto, BatteryStatsHelper helper) {
         final long sToken = proto.start(BatteryStatsProto.SYSTEM);
         final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
         final long rawRealtimeMs = SystemClock.elapsedRealtime();
@@ -6637,7 +7088,7 @@
         final int which = STATS_SINCE_CHARGED;
 
         // Battery data (BATTERY_DATA)
-        long token = proto.start(SystemProto.BATTERY);
+        final long bToken = proto.start(SystemProto.BATTERY);
         proto.write(SystemProto.Battery.START_CLOCK_TIME_MS, getStartClockTime());
         proto.write(SystemProto.Battery.START_COUNT, getStartCount());
         proto.write(SystemProto.Battery.TOTAL_REALTIME_MS,
@@ -6660,10 +7111,10 @@
                 getMinLearnedBatteryCapacity());
         proto.write(SystemProto.Battery.MAX_LEARNED_BATTERY_CAPACITY_UAH,
                 getMaxLearnedBatteryCapacity());
-        proto.end(token);
+        proto.end(bToken);
 
         // Battery discharge (BATTERY_DISCHARGE_DATA)
-        token = proto.start(SystemProto.BATTERY_DISCHARGE);
+        final long bdToken = proto.start(SystemProto.BATTERY_DISCHARGE);
         proto.write(SystemProto.BatteryDischarge.LOWER_BOUND_SINCE_CHARGE,
                 getLowDischargeAmountSinceCharge());
         proto.write(SystemProto.BatteryDischarge.UPPER_BOUND_SINCE_CHARGE,
@@ -6680,10 +7131,11 @@
                 getUahDischargeScreenOff(which) / 1000);
         proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_DOZE,
                 getUahDischargeScreenDoze(which) / 1000);
-        proto.end(token);
+        proto.end(bdToken);
 
         // Time remaining
         long timeRemainingUs = computeChargeTimeRemaining(rawRealtimeUs);
+        // These are part of a oneof, so we should only set one of them.
         if (timeRemainingUs >= 0) {
             // Charge time remaining (CHARGE_TIME_REMAIN_DATA)
             proto.write(SystemProto.CHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
@@ -6702,11 +7154,11 @@
 
         // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
         for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
-            token = proto.start(SystemProto.DATA_CONNECTION);
+            final long pdcToken = proto.start(SystemProto.DATA_CONNECTION);
             proto.write(SystemProto.DataConnection.NAME, i);
             dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(pdcToken);
         }
 
         // Discharge step (DISCHARGE_STEP_DATA)
@@ -6729,7 +7181,7 @@
                 getModemControllerActivity(), which);
 
         // Global network data (GLOBAL_NETWORK_DATA)
-        token = proto.start(SystemProto.GLOBAL_NETWORK);
+        final long gnToken = proto.start(SystemProto.GLOBAL_NETWORK);
         proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_RX,
                 getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
         proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_TX,
@@ -6750,7 +7202,7 @@
                 getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
         proto.write(SystemProto.GlobalNetwork.BT_BYTES_TX,
                 getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
-        proto.end(token);
+        proto.end(gnToken);
 
         // Wifi controller (GLOBAL_WIFI_CONTROLLER_DATA)
         dumpControllerActivityProto(proto, SystemProto.GLOBAL_WIFI_CONTROLLER,
@@ -6758,21 +7210,21 @@
 
 
         // Global wifi (GLOBAL_WIFI_DATA)
-        token = proto.start(SystemProto.GLOBAL_WIFI);
+        final long gwToken = proto.start(SystemProto.GLOBAL_WIFI);
         proto.write(SystemProto.GlobalWifi.ON_DURATION_MS,
                 getWifiOnTime(rawRealtimeUs, which) / 1000);
         proto.write(SystemProto.GlobalWifi.RUNNING_DURATION_MS,
                 getGlobalWifiRunningTime(rawRealtimeUs, which) / 1000);
-        proto.end(token);
+        proto.end(gwToken);
 
         // Kernel wakelock (KERNEL_WAKELOCK_DATA)
         final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
         for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
-            token = proto.start(SystemProto.KERNEL_WAKELOCK);
+            final long kwToken = proto.start(SystemProto.KERNEL_WAKELOCK);
             proto.write(SystemProto.KernelWakelock.NAME, ent.getKey());
             dumpTimer(proto, SystemProto.KernelWakelock.TOTAL, ent.getValue(),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(kwToken);
         }
 
         // Misc (MISC_DATA)
@@ -6802,7 +7254,7 @@
                 }
             }
         }
-        token = proto.start(SystemProto.MISC);
+        final long mToken = proto.start(SystemProto.MISC);
         proto.write(SystemProto.Misc.SCREEN_ON_DURATION_MS,
                 getScreenOnTime(rawRealtimeUs, which) / 1000);
         proto.write(SystemProto.Misc.PHONE_ON_DURATION_MS,
@@ -6845,11 +7297,7 @@
                 getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
         proto.write(SystemProto.Misc.LONGEST_LIGHT_DOZE_DURATION_MS,
                 getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
-        proto.end(token);
-
-        final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
-        helper.create(this);
-        helper.refreshStats(which, UserHandle.USER_ALL);
+        proto.end(mToken);
 
         // Power use item (POWER_USE_ITEM_DATA)
         final List<BatterySipper> sippers = helper.getUsageList();
@@ -6881,7 +7329,7 @@
                         n = SystemProto.PowerUseItem.FLASHLIGHT;
                         break;
                     case APP:
-                        // dumpProtoAppLocked will handle this.
+                        // dumpProtoAppsLocked will handle this.
                         continue;
                     case USER:
                         n = SystemProto.PowerUseItem.USER;
@@ -6900,7 +7348,7 @@
                         n = SystemProto.PowerUseItem.MEMORY;
                         break;
                 }
-                token = proto.start(SystemProto.POWER_USE_ITEM);
+                final long puiToken = proto.start(SystemProto.POWER_USE_ITEM);
                 proto.write(SystemProto.PowerUseItem.NAME, n);
                 proto.write(SystemProto.PowerUseItem.UID, uid);
                 proto.write(SystemProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
@@ -6908,39 +7356,39 @@
                 proto.write(SystemProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
                 proto.write(SystemProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
                         bs.proportionalSmearMah);
-                proto.end(token);
+                proto.end(puiToken);
             }
         }
 
         // Power use summary (POWER_USE_SUMMARY_DATA)
-        token = proto.start(SystemProto.POWER_USE_SUMMARY);
+        final long pusToken = proto.start(SystemProto.POWER_USE_SUMMARY);
         proto.write(SystemProto.PowerUseSummary.BATTERY_CAPACITY_MAH,
                 helper.getPowerProfile().getBatteryCapacity());
         proto.write(SystemProto.PowerUseSummary.COMPUTED_POWER_MAH, helper.getComputedPower());
         proto.write(SystemProto.PowerUseSummary.MIN_DRAINED_POWER_MAH, helper.getMinDrainedPower());
         proto.write(SystemProto.PowerUseSummary.MAX_DRAINED_POWER_MAH, helper.getMaxDrainedPower());
-        proto.end(token);
+        proto.end(pusToken);
 
         // RPM stats (RESOURCE_POWER_MANAGER_DATA)
         final Map<String, ? extends Timer> rpmStats = getRpmStats();
         final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
         for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
-            token = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
+            final long rpmToken = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
             proto.write(SystemProto.ResourcePowerManager.NAME, ent.getKey());
             dumpTimer(proto, SystemProto.ResourcePowerManager.TOTAL,
                     ent.getValue(), rawRealtimeUs, which);
             dumpTimer(proto, SystemProto.ResourcePowerManager.SCREEN_OFF,
                     screenOffRpmStats.get(ent.getKey()), rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(rpmToken);
         }
 
         // Screen brightness (SCREEN_BRIGHTNESS_DATA)
         for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; ++i) {
-            token = proto.start(SystemProto.SCREEN_BRIGHTNESS);
+            final long sbToken = proto.start(SystemProto.SCREEN_BRIGHTNESS);
             proto.write(SystemProto.ScreenBrightness.NAME, i);
             dumpTimer(proto, SystemProto.ScreenBrightness.TOTAL, getScreenBrightnessTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(sbToken);
         }
 
         // Signal scanning time (SIGNAL_SCANNING_TIME_DATA)
@@ -6949,47 +7397,47 @@
 
         // Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA)
         for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; ++i) {
-            token = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
+            final long pssToken = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
             proto.write(SystemProto.PhoneSignalStrength.NAME, i);
             dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(pssToken);
         }
 
         // Wakeup reasons (WAKEUP_REASON_DATA)
         final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
         for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
-            token = proto.start(SystemProto.WAKEUP_REASON);
+            final long wrToken = proto.start(SystemProto.WAKEUP_REASON);
             proto.write(SystemProto.WakeupReason.NAME, ent.getKey());
             dumpTimer(proto, SystemProto.WakeupReason.TOTAL, ent.getValue(), rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(wrToken);
         }
 
         // Wifi signal strength (WIFI_SIGNAL_STRENGTH_TIME_DATA and WIFI_SIGNAL_STRENGTH_COUNT_DATA)
         for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; ++i) {
-            token = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
+            final long wssToken = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
             proto.write(SystemProto.WifiSignalStrength.NAME, i);
             dumpTimer(proto, SystemProto.WifiSignalStrength.TOTAL, getWifiSignalStrengthTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(wssToken);
         }
 
         // Wifi state (WIFI_STATE_TIME_DATA and WIFI_STATE_COUNT_DATA)
         for (int i = 0; i < NUM_WIFI_STATES; ++i) {
-            token = proto.start(SystemProto.WIFI_STATE);
+            final long wsToken = proto.start(SystemProto.WIFI_STATE);
             proto.write(SystemProto.WifiState.NAME, i);
             dumpTimer(proto, SystemProto.WifiState.TOTAL, getWifiStateTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(wsToken);
         }
 
         // Wifi supplicant state (WIFI_SUPPL_STATE_TIME_DATA and WIFI_SUPPL_STATE_COUNT_DATA)
         for (int i = 0; i < NUM_WIFI_SUPPL_STATES; ++i) {
-            token = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
+            final long wssToken = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
             proto.write(SystemProto.WifiSupplicantState.NAME, i);
             dumpTimer(proto, SystemProto.WifiSupplicantState.TOTAL, getWifiSupplStateTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(wssToken);
         }
 
         proto.end(sToken);
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index d8f9567..20f6c8e 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.os.StatsLogEventWrapper;
+
 /**
   * Binder interface to communicate with the Java-based statistics service helper.
   * {@hide}
@@ -50,5 +52,5 @@
     oneway void cancelPollingAlarms();
 
     /** Pull the specified data. Results will be sent to statsd when complete. */
-    String pullData(int pullCode);
+    StatsLogEventWrapper[] pullData(int pullCode);
 }
diff --git a/core/java/android/service/autofill/SaveInfo.aidl b/core/java/android/os/StatsLogEventWrapper.aidl
similarity index 69%
rename from core/java/android/service/autofill/SaveInfo.aidl
rename to core/java/android/os/StatsLogEventWrapper.aidl
index 8cda608..766343e 100644
--- a/core/java/android/service/autofill/SaveInfo.aidl
+++ b/core/java/android/os/StatsLogEventWrapper.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
-package android.service.autofill;
+package android.os;
 
-parcelable SaveInfo;
+/** @hide */
+parcelable StatsLogEventWrapper cpp_header "android/os/StatsLogEventWrapper.h";
\ No newline at end of file
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
new file mode 100644
index 0000000..9491bec
--- /dev/null
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Wrapper class for sending data from Android OS to StatsD.
+ *
+ * @hide
+ */
+public final class StatsLogEventWrapper implements Parcelable {
+    private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
+
+    // Below are constants copied from log/log.h
+    private static final int EVENT_TYPE_INT = 0;  /* int32_t */
+    private static final int EVENT_TYPE_LONG = 1; /* int64_t */
+    private static final int EVENT_TYPE_STRING = 2;
+    private static final int EVENT_TYPE_LIST = 3;
+    private static final int EVENT_TYPE_FLOAT = 4;
+
+    /**
+     * Creates a log_event that is binary-encoded as implemented in
+     * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
+     * for pushed and pulled data. The write* methods must be called in the same order as their
+     * field number. There is no checking that the correct number of write* methods is called.
+     * We also write an END_LIST character before beginning to write to parcel, but this END_LIST
+     * may be unnecessary.
+     *
+     * @param tag    The integer representing the tag for this event.
+     * @param fields The number of fields specified in this event.
+     */
+    public StatsLogEventWrapper(int tag, int fields) {
+        // Write four bytes from tag, starting with least-significant bit.
+        write4Bytes(tag);
+        mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
+        mStorage.write(fields); // Indicate number of elements in this list.
+    }
+
+    /**
+     * Boilerplate for Parcel.
+     */
+    public static final Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
+            Parcelable.Creator<StatsLogEventWrapper>() {
+                public StatsLogEventWrapper createFromParcel(Parcel in) {
+                    return new StatsLogEventWrapper(in);
+                }
+
+                public StatsLogEventWrapper[] newArray(int size) {
+                    return new StatsLogEventWrapper[size];
+                }
+            };
+
+    private void write4Bytes(int val) {
+        mStorage.write(val);
+        mStorage.write(val >>> 8);
+        mStorage.write(val >>> 16);
+        mStorage.write(val >>> 24);
+    }
+
+    private void write8Bytes(long val) {
+        write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
+        write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
+    }
+
+    /**
+     * Adds 32-bit integer to output.
+     */
+    public void writeInt(int val) {
+        mStorage.write(EVENT_TYPE_INT);
+        write4Bytes(val);
+    }
+
+    /**
+     * Adds 64-bit long to output.
+     */
+    public void writeLong(long val) {
+        mStorage.write(EVENT_TYPE_LONG);
+        write8Bytes(val);
+    }
+
+    /**
+     * Adds a 4-byte floating point value to output.
+     */
+    public void writeFloat(float val) {
+        int v = Float.floatToIntBits(val);
+        mStorage.write(EVENT_TYPE_FLOAT);
+        write4Bytes(v);
+    }
+
+    /**
+     * Adds a string to the output.
+     */
+    public void writeString(String val) {
+        mStorage.write(EVENT_TYPE_STRING);
+        write4Bytes(val.length());
+        byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
+        mStorage.write(bytes, 0, bytes.length);
+    }
+
+    private StatsLogEventWrapper(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Writes the stored fields to a byte array. Will first write a new-line character to denote
+     * END_LIST before writing contents to byte array.
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        mStorage.write(10); // new-line character is same as END_LIST
+        out.writeByteArray(mStorage.toByteArray());
+    }
+
+    /**
+     * Not implemented.
+     */
+    public void readFromParcel(Parcel in) {
+        // Not needed since this java class is for sending to statsd only.
+    }
+
+    /**
+     * Boilerplate for Parcel.
+     */
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/service/autofill/Dataset.aidl b/core/java/android/service/autofill/Dataset.aidl
deleted file mode 100644
index 2342c5f..0000000
--- a/core/java/android/service/autofill/Dataset.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.autofill;
-
-parcelable Dataset;
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index b2cdef2..331130e 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -150,8 +150,16 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        return new StringBuilder("Dataset " + mId + " [")
-                .append("fieldIds=").append(mFieldIds)
+        final StringBuilder builder = new StringBuilder("Dataset[id=");
+        if (mId == null) {
+            builder.append("null");
+        } else {
+            // Cannot disclose id because it could contain PII.
+            builder.append(mId.length()).append("_chars");
+        }
+
+        return builder
+                .append(", fieldIds=").append(mFieldIds)
                 .append(", fieldValues=").append(mFieldValues)
                 .append(", fieldPresentations=")
                 .append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index d6c0dbf..b1857b3 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -129,6 +129,11 @@
     }
 
     @Override
+    public String toString() {
+        return mEvents == null ? "no events" : mEvents.toString();
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index d91cebb..d2033fa 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -71,7 +71,7 @@
     private FillResponse(@NonNull Builder builder) {
         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
         mSaveInfo = builder.mSaveInfo;
-        mClientState = builder.mCLientState;
+        mClientState = builder.mClientState;
         mPresentation = builder.mPresentation;
         mAuthentication = builder.mAuthentication;
         mAuthenticationIds = builder.mAuthenticationIds;
@@ -145,7 +145,7 @@
     public static final class Builder {
         private ArrayList<Dataset> mDatasets;
         private SaveInfo mSaveInfo;
-        private Bundle mCLientState;
+        private Bundle mClientState;
         private RemoteViews mPresentation;
         private IntentSender mAuthentication;
         private AutofillId[] mAuthenticationIds;
@@ -288,7 +288,7 @@
          */
         public Builder setClientState(@Nullable Bundle clientState) {
             throwIfDestroyed();
-            mCLientState = clientState;
+            mClientState = clientState;
             return this;
         }
 
diff --git a/core/java/android/view/autofill/AutoFillType.aidl b/core/java/android/view/autofill/AutoFillType.aidl
deleted file mode 100644
index 4606b48..0000000
--- a/core/java/android/view/autofill/AutoFillType.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-/*
- * TODO(b/35956626): remove once clients use getAutoFilltype()
- */
-parcelable AutoFillType;
\ No newline at end of file
diff --git a/core/java/android/view/autofill/AutoFillValue.aidl b/core/java/android/view/autofill/AutoFillValue.aidl
deleted file mode 100644
index 05b7562..0000000
--- a/core/java/android/view/autofill/AutoFillValue.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-//  @deprecated TODO(b/35956626): remove once clients use AutofillValue
-parcelable AutoFillValue;
\ No newline at end of file
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index af09592..384f4f8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -476,17 +476,6 @@
         stopTextActionModeWithPreservingSelection();
     }
 
-    void invalidateMagnifier() {
-        final DisplayMetrics dm = mTextView.getResources().getDisplayMetrics();
-        invalidateMagnifier(0, 0, dm.widthPixels, dm.heightPixels);
-    }
-
-    void invalidateMagnifier(final float l, final float t, final float r, final float b) {
-        if (mMagnifier != null) {
-            mTextView.post(() -> mMagnifier.invalidate(new RectF(l, t, r, b)));
-        }
-    }
-
     private void discardTextDisplayLists() {
         if (mTextRenderNodes != null) {
             for (int i = 0; i < mTextRenderNodes.length; i++) {
@@ -4550,17 +4539,15 @@
             final Layout layout = mTextView.getLayout();
             final int lineNumber = layout.getLineForOffset(offset);
             // Horizontally snap to character offset.
-            final float xPosInView = getHorizontal(mTextView.getLayout(), offset);
+            final float xPosInView = getHorizontal(mTextView.getLayout(), offset)
+                    + mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
             // Vertically snap to middle of current line.
             final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber)
-                    + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f;
-            final int[] coordinatesOnScreen = new int[2];
-            mTextView.getLocationOnScreen(coordinatesOnScreen);
-            final float centerXOnScreen = mTextView.convertViewToScreenCoord(xPosInView, true);
-            final float centerYOnScreen = mTextView.convertViewToScreenCoord(yPosInView, false);
+                    + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
+                    + mTextView.getTotalPaddingTop() - mTextView.getScrollY();
 
             suspendBlink();
-            mMagnifier.show(centerXOnScreen, centerYOnScreen, MAGNIFIER_ZOOM);
+            mMagnifier.show(xPosInView, yPosInView, MAGNIFIER_ZOOM);
         }
 
         protected final void dismissMagnifier() {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 51277e4..e5ae0ca 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -16,11 +16,14 @@
 
 package android.widget;
 
-import android.Manifest;
+import android.annotation.WorkerThread;
+import android.app.IServiceConnection;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -29,7 +32,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -39,7 +41,6 @@
 import android.view.ViewGroup;
 import android.widget.RemoteViews.OnClickHandler;
 
-import com.android.internal.widget.IRemoteViewsAdapterConnection;
 import com.android.internal.widget.IRemoteViewsFactory;
 
 import java.lang.ref.WeakReference;
@@ -49,52 +50,33 @@
 import java.util.concurrent.Executor;
 
 /**
- * An adapter to a RemoteViewsService which fetches and caches RemoteViews
- * to be later inflated as child views.
+ * An adapter to a RemoteViewsService which fetches and caches RemoteViews to be later inflated as
+ * child views.
+ *
+ * The adapter runs in the host process, typically a Launcher app.
+ *
+ * It makes a service connection to the {@link RemoteViewsService} running in the
+ * AppWidgetsProvider's process. This connection is made on a background thread (and proxied via
+ * the platform to get the bind permissions) and all interaction with the service is done on the
+ * background thread.
+ *
+ * On first bind, the adapter will load can cache the RemoteViews locally. Afterwards the
+ * connection is only made when new RemoteViews are required.
+ * @hide
  */
-/** @hide */
 public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
-    private static final String MULTI_USER_PERM = Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 
     private static final String TAG = "RemoteViewsAdapter";
 
     // The max number of items in the cache
-    private static final int sDefaultCacheSize = 40;
+    private static final int DEFAULT_CACHE_SIZE = 40;
     // The delay (in millis) to wait until attempting to unbind from a service after a request.
     // This ensures that we don't stay continually bound to the service and that it can be destroyed
     // if we need the memory elsewhere in the system.
-    private static final int sUnbindServiceDelay = 5000;
+    private static final int UNBIND_SERVICE_DELAY = 5000;
 
     // Default height for the default loading view, in case we cannot get inflate the first view
-    private static final int sDefaultLoadingViewHeight = 50;
-
-    // Type defs for controlling different messages across the main and worker message queues
-    private static final int sDefaultMessageType = 0;
-    private static final int sUnbindServiceMessageType = 1;
-
-    private final Context mContext;
-    private final Intent mIntent;
-    private final int mAppWidgetId;
-    private final Executor mAsyncViewLoadExecutor;
-
-    private RemoteViewsAdapterServiceConnection mServiceConnection;
-    private WeakReference<RemoteAdapterConnectionCallback> mCallback;
-    private OnClickHandler mRemoteViewsOnClickHandler;
-    private final FixedSizeRemoteViewsCache mCache;
-    private int mVisibleWindowLowerBound;
-    private int mVisibleWindowUpperBound;
-
-    // A flag to determine whether we should notify data set changed after we connect
-    private boolean mNotifyDataSetChangedAfterOnServiceConnected = false;
-
-    // The set of requested views that are to be notified when the associated RemoteViews are
-    // loaded.
-    private RemoteViewsFrameLayoutRefSet mRequestedViews;
-
-    private HandlerThread mWorkerThread;
-    // items may be interrupted within the normally processed queues
-    private Handler mWorkerQueue;
-    private Handler mMainQueue;
+    private static final int DEFAULT_LOADING_VIEW_HEIGHT = 50;
 
     // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
     // structures;
@@ -111,6 +93,26 @@
     // duration, the cache is dropped.
     private static final int REMOTE_VIEWS_CACHE_DURATION = 5000;
 
+    private final Context mContext;
+    private final Intent mIntent;
+    private final int mAppWidgetId;
+    private final Executor mAsyncViewLoadExecutor;
+
+    private OnClickHandler mRemoteViewsOnClickHandler;
+    private final FixedSizeRemoteViewsCache mCache;
+    private int mVisibleWindowLowerBound;
+    private int mVisibleWindowUpperBound;
+
+    // The set of requested views that are to be notified when the associated RemoteViews are
+    // loaded.
+    private RemoteViewsFrameLayoutRefSet mRequestedViews;
+
+    private final HandlerThread mWorkerThread;
+    // items may be interrupted within the normally processed queues
+    private final Handler mMainHandler;
+    private final RemoteServiceHandler mServiceHandler;
+    private final RemoteAdapterConnectionCallback mCallback;
+
     // Used to indicate to the AdapterView that it can use this Adapter immediately after
     // construction (happens when we have a cached FixedSizeRemoteViewsCache).
     private boolean mDataReady = false;
@@ -158,154 +160,192 @@
         }
     }
 
+    static final int MSG_REQUEST_BIND = 1;
+    static final int MSG_NOTIFY_DATA_SET_CHANGED = 2;
+    static final int MSG_LOAD_NEXT_ITEM = 3;
+    static final int MSG_UNBIND_SERVICE = 4;
+
+    private static final int MSG_MAIN_HANDLER_COMMIT_METADATA = 1;
+    private static final int MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED = 2;
+    private static final int MSG_MAIN_HANDLER_REMOTE_ADAPTER_CONNECTED = 3;
+    private static final int MSG_MAIN_HANDLER_REMOTE_ADAPTER_DISCONNECTED = 4;
+    private static final int MSG_MAIN_HANDLER_REMOTE_VIEWS_LOADED = 5;
+
     /**
-     * The service connection that gets populated when the RemoteViewsService is
-     * bound.  This must be a static inner class to ensure that no references to the outer
-     * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being
-     * garbage collected, and would cause us to leak activities due to the caching mechanism for
-     * FrameLayouts in the adapter).
+     * Handler for various interactions with the {@link RemoteViewsService}.
      */
-    private static class RemoteViewsAdapterServiceConnection extends
-            IRemoteViewsAdapterConnection.Stub {
-        private boolean mIsConnected;
-        private boolean mIsConnecting;
-        private WeakReference<RemoteViewsAdapter> mAdapter;
+    private static class RemoteServiceHandler extends Handler implements ServiceConnection {
+
+        private final WeakReference<RemoteViewsAdapter> mAdapter;
+        private final Context mContext;
+
         private IRemoteViewsFactory mRemoteViewsFactory;
 
-        public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) {
-            mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
+        // The last call to notifyDataSetChanged didn't succeed, try again on next service bind.
+        private boolean mNotifyDataSetChangedPending = false;
+        private boolean mBindRequested = false;
+
+        RemoteServiceHandler(Looper workerLooper, RemoteViewsAdapter adapter, Context context) {
+            super(workerLooper);
+            mAdapter = new WeakReference<>(adapter);
+            mContext = context;
         }
 
-        public synchronized void bind(Context context, int appWidgetId, Intent intent) {
-            if (!mIsConnecting) {
-                try {
-                    RemoteViewsAdapter adapter;
-                    final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
-                    if ((adapter = mAdapter.get()) != null) {
-                        mgr.bindRemoteViewsService(context.getOpPackageName(), appWidgetId,
-                                intent, asBinder());
-                    } else {
-                        Slog.w(TAG, "bind: adapter was null");
-                    }
-                    mIsConnecting = true;
-                } catch (Exception e) {
-                    Log.e("RVAServiceConnection", "bind(): " + e.getMessage());
-                    mIsConnecting = false;
-                    mIsConnected = false;
-                }
-            }
-        }
-
-        public synchronized void unbind(Context context, int appWidgetId, Intent intent) {
-            try {
-                RemoteViewsAdapter adapter;
-                final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
-                if ((adapter = mAdapter.get()) != null) {
-                    mgr.unbindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent);
-                } else {
-                    Slog.w(TAG, "unbind: adapter was null");
-                }
-                mIsConnecting = false;
-            } catch (Exception e) {
-                Log.e("RVAServiceConnection", "unbind(): " + e.getMessage());
-                mIsConnecting = false;
-                mIsConnected = false;
-            }
-        }
-
-        public synchronized void onServiceConnected(IBinder service) {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            // This is called on the same thread.
             mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
+            enqueueDeferredUnbindServiceMessage();
 
-            // Remove any deferred unbind messages
-            final RemoteViewsAdapter adapter = mAdapter.get();
-            if (adapter == null) return;
+            RemoteViewsAdapter adapter = mAdapter.get();
+            if (adapter == null) {
+                return;
+            }
 
-            // Queue up work that we need to do for the callback to run
-            adapter.mWorkerQueue.post(new Runnable() {
-                @Override
-                public void run() {
-                    if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) {
-                        // Handle queued notifyDataSetChanged() if necessary
-                        adapter.onNotifyDataSetChanged();
-                    } else {
-                        IRemoteViewsFactory factory =
-                            adapter.mServiceConnection.getRemoteViewsFactory();
-                        try {
-                            if (!factory.isCreated()) {
-                                // We only call onDataSetChanged() if this is the factory was just
-                                // create in response to this bind
-                                factory.onDataSetChanged();
-                            }
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Error notifying factory of data set changed in " +
-                                        "onServiceConnected(): " + e.getMessage());
-
-                            // Return early to prevent anything further from being notified
-                            // (effectively nothing has changed)
-                            return;
-                        } catch (RuntimeException e) {
-                            Log.e(TAG, "Error notifying factory of data set changed in " +
-                                    "onServiceConnected(): " + e.getMessage());
-                        }
-
-                        // Request meta data so that we have up to date data when calling back to
-                        // the remote adapter callback
-                        adapter.updateTemporaryMetaData();
-
-                        // Notify the host that we've connected
-                        adapter.mMainQueue.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                synchronized (adapter.mCache) {
-                                    adapter.mCache.commitTemporaryMetaData();
-                                }
-
-                                final RemoteAdapterConnectionCallback callback =
-                                    adapter.mCallback.get();
-                                if (callback != null) {
-                                    callback.onRemoteAdapterConnected();
-                                }
-                            }
-                        });
-                    }
-
-                    // Enqueue unbind message
-                    adapter.enqueueDeferredUnbindServiceMessage();
-                    mIsConnected = true;
-                    mIsConnecting = false;
+            if (mNotifyDataSetChangedPending) {
+                mNotifyDataSetChangedPending = false;
+                Message msg = Message.obtain(this, MSG_NOTIFY_DATA_SET_CHANGED);
+                handleMessage(msg);
+                msg.recycle();
+            } else {
+                if (!sendNotifyDataSetChange(false)) {
+                    return;
                 }
-            });
+
+                // Request meta data so that we have up to date data when calling back to
+                // the remote adapter callback
+                adapter.updateTemporaryMetaData(mRemoteViewsFactory);
+                adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_COMMIT_METADATA);
+                adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_REMOTE_ADAPTER_CONNECTED);
+            }
         }
 
-        public synchronized void onServiceDisconnected() {
-            mIsConnected = false;
-            mIsConnecting = false;
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
             mRemoteViewsFactory = null;
+            RemoteViewsAdapter adapter = mAdapter.get();
+            if (adapter != null) {
+                adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_REMOTE_ADAPTER_DISCONNECTED);
+            }
+        }
 
-            // Clear the main/worker queues
-            final RemoteViewsAdapter adapter = mAdapter.get();
-            if (adapter == null) return;
+        @Override
+        public void handleMessage(Message msg) {
+            RemoteViewsAdapter adapter = mAdapter.get();
 
-            adapter.mMainQueue.post(new Runnable() {
-                @Override
-                public void run() {
-                    // Dequeue any unbind messages
-                    adapter.mMainQueue.removeMessages(sUnbindServiceMessageType);
-
-                    final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
-                    if (callback != null) {
-                        callback.onRemoteAdapterDisconnected();
+            switch (msg.what) {
+                case MSG_REQUEST_BIND: {
+                    if (adapter == null || mRemoteViewsFactory != null) {
+                        enqueueDeferredUnbindServiceMessage();
                     }
+                    if (mBindRequested) {
+                        return;
+                    }
+                    int flags = Context.BIND_AUTO_CREATE
+                            | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE;
+                    final IServiceConnection sd = mContext.getServiceDispatcher(this, this, flags);
+                    Intent intent = (Intent) msg.obj;
+                    int appWidgetId = msg.arg1;
+                    mBindRequested = AppWidgetManager.getInstance(mContext)
+                            .bindRemoteViewsService(mContext, appWidgetId, intent, sd, flags);
+                    return;
                 }
-            });
+                case MSG_NOTIFY_DATA_SET_CHANGED: {
+                    enqueueDeferredUnbindServiceMessage();
+                    if (adapter == null) {
+                        return;
+                    }
+                    if (mRemoteViewsFactory == null) {
+                        mNotifyDataSetChangedPending = true;
+                        adapter.requestBindService();
+                        return;
+                    }
+                    if (!sendNotifyDataSetChange(true)) {
+                        return;
+                    }
+
+                    // Flush the cache so that we can reload new items from the service
+                    synchronized (adapter.mCache) {
+                        adapter.mCache.reset();
+                    }
+
+                    // Re-request the new metadata (only after the notification to the factory)
+                    adapter.updateTemporaryMetaData(mRemoteViewsFactory);
+                    int newCount;
+                    int[] visibleWindow;
+                    synchronized (adapter.mCache.getTemporaryMetaData()) {
+                        newCount = adapter.mCache.getTemporaryMetaData().count;
+                        visibleWindow = adapter.getVisibleWindow(newCount);
+                    }
+
+                    // Pre-load (our best guess of) the views which are currently visible in the
+                    // AdapterView. This mitigates flashing and flickering of loading views when a
+                    // widget notifies that its data has changed.
+                    for (int position : visibleWindow) {
+                        // Because temporary meta data is only ever modified from this thread
+                        // (ie. mWorkerThread), it is safe to assume that count is a valid
+                        // representation.
+                        if (position < newCount) {
+                            adapter.updateRemoteViews(mRemoteViewsFactory, position, false);
+                        }
+                    }
+
+                    // Propagate the notification back to the base adapter
+                    adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_COMMIT_METADATA);
+                    adapter.mMainHandler.sendEmptyMessage(
+                            MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED);
+                    return;
+                }
+
+                case MSG_LOAD_NEXT_ITEM: {
+                    if (adapter == null || mRemoteViewsFactory == null) {
+                        return;
+                    }
+                    removeMessages(MSG_UNBIND_SERVICE);
+                    // Get the next index to load
+                    final int position = adapter.mCache.getNextIndexToLoad();
+                    if (position > -1) {
+                        // Load the item, and notify any existing RemoteViewsFrameLayouts
+                        adapter.updateRemoteViews(mRemoteViewsFactory, position, true);
+
+                        // Queue up for the next one to load
+                        sendEmptyMessage(MSG_LOAD_NEXT_ITEM);
+                    } else {
+                        // No more items to load, so queue unbind
+                        enqueueDeferredUnbindServiceMessage();
+                    }
+                    return;
+                }
+                case MSG_UNBIND_SERVICE: {
+                    unbindNow();
+                    return;
+                }
+            }
         }
 
-        public synchronized IRemoteViewsFactory getRemoteViewsFactory() {
-            return mRemoteViewsFactory;
+        protected void unbindNow() {
+            if (mBindRequested) {
+                mBindRequested = false;
+                mContext.unbindService(this);
+            }
+            mRemoteViewsFactory = null;
         }
 
-        public synchronized boolean isConnected() {
-            return mIsConnected;
+        private boolean sendNotifyDataSetChange(boolean always) {
+            try {
+                if (always || !mRemoteViewsFactory.isCreated()) {
+                    mRemoteViewsFactory.onDataSetChanged();
+                }
+                return true;
+            } catch (RemoteException | RuntimeException e) {
+                Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
+                return false;
+            }
+        }
+
+        private void enqueueDeferredUnbindServiceMessage() {
+            removeMessages(MSG_UNBIND_SERVICE);
+            sendEmptyMessageDelayed(MSG_UNBIND_SERVICE, UNBIND_SERVICE_DELAY);
         }
     }
 
@@ -507,7 +547,6 @@
      *
      */
     private static class FixedSizeRemoteViewsCache {
-        private static final String TAG = "FixedSizeRemoteViewsCache";
 
         // The meta data related to all the RemoteViews, ie. count, is stable, etc.
         // The meta data objects are made final so that they can be locked on independently
@@ -671,7 +710,7 @@
                 }
             }
 
-            int count = 0;
+            int count;
             synchronized (mMetaData) {
                 count = mMetaData.count;
             }
@@ -786,9 +825,11 @@
         // Initialize the worker thread
         mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
         mWorkerThread.start();
-        mWorkerQueue = new Handler(mWorkerThread.getLooper());
-        mMainQueue = new Handler(Looper.myLooper(), this);
+        mMainHandler = new Handler(Looper.myLooper(), this);
+        mServiceHandler = new RemoteServiceHandler(mWorkerThread.getLooper(), this,
+                context.getApplicationContext());
         mAsyncViewLoadExecutor = useAsyncLoader ? new HandlerThreadExecutor(mWorkerThread) : null;
+        mCallback = callback;
 
         if (sCacheRemovalThread == null) {
             sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner");
@@ -796,10 +837,6 @@
             sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper());
         }
 
-        // Initialize the cache and the service connection on startup
-        mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
-        mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
-
         RemoteViewsCacheKey key = new RemoteViewsCacheKey(new Intent.FilterComparison(mIntent),
                 mAppWidgetId);
 
@@ -814,7 +851,7 @@
                     }
                 }
             } else {
-                mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
+                mCache = new FixedSizeRemoteViewsCache(DEFAULT_CACHE_SIZE);
             }
             if (!mDataReady) {
                 requestBindService();
@@ -825,9 +862,8 @@
     @Override
     protected void finalize() throws Throwable {
         try {
-            if (mWorkerThread != null) {
-                mWorkerThread.quit();
-            }
+            mServiceHandler.unbindNow();
+            mWorkerThread.quit();
         } finally {
             super.finalize();
         }
@@ -864,16 +900,13 @@
                 sCachedRemoteViewsCaches.put(key, mCache);
             }
 
-            Runnable r = new Runnable() {
-                @Override
-                public void run() {
-                    synchronized (sCachedRemoteViewsCaches) {
-                        if (sCachedRemoteViewsCaches.containsKey(key)) {
-                            sCachedRemoteViewsCaches.remove(key);
-                        }
-                        if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
-                            sRemoteViewsCacheRemoveRunnables.remove(key);
-                        }
+            Runnable r = () -> {
+                synchronized (sCachedRemoteViewsCaches) {
+                    if (sCachedRemoteViewsCaches.containsKey(key)) {
+                        sCachedRemoteViewsCaches.remove(key);
+                    }
+                    if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
+                        sRemoteViewsCacheRemoveRunnables.remove(key);
                     }
                 }
             };
@@ -882,54 +915,8 @@
         }
     }
 
-    private void loadNextIndexInBackground() {
-        mWorkerQueue.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mServiceConnection.isConnected()) {
-                    // Get the next index to load
-                    int position = -1;
-                    synchronized (mCache) {
-                        position = mCache.getNextIndexToLoad();
-                    }
-                    if (position > -1) {
-                        // Load the item, and notify any existing RemoteViewsFrameLayouts
-                        updateRemoteViews(position, true);
-
-                        // Queue up for the next one to load
-                        loadNextIndexInBackground();
-                    } else {
-                        // No more items to load, so queue unbind
-                        enqueueDeferredUnbindServiceMessage();
-                    }
-                }
-            }
-        });
-    }
-
-    private void processException(String method, Exception e) {
-        Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage());
-
-        // If we encounter a crash when updating, we should reset the metadata & cache and trigger
-        // a notifyDataSetChanged to update the widget accordingly
-        final RemoteViewsMetaData metaData = mCache.getMetaData();
-        synchronized (metaData) {
-            metaData.reset();
-        }
-        synchronized (mCache) {
-            mCache.reset();
-        }
-        mMainQueue.post(new Runnable() {
-            @Override
-            public void run() {
-                superNotifyDataSetChanged();
-            }
-        });
-    }
-
-    private void updateTemporaryMetaData() {
-        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
-
+    @WorkerThread
+    private void updateTemporaryMetaData(IRemoteViewsFactory factory) {
         try {
             // get the properties/first view (so that we can use it to
             // measure our dummy views)
@@ -953,40 +940,40 @@
                 tmpMetaData.count = count;
                 tmpMetaData.loadingTemplate = loadingTemplate;
             }
-        } catch(RemoteException e) {
-            processException("updateMetaData", e);
-        } catch(RuntimeException e) {
-            processException("updateMetaData", e);
+        } catch (RemoteException | RuntimeException e) {
+            Log.e("RemoteViewsAdapter", "Error in updateMetaData: " + e.getMessage());
+
+            // If we encounter a crash when updating, we should reset the metadata & cache
+            // and trigger a notifyDataSetChanged to update the widget accordingly
+            synchronized (mCache.getMetaData()) {
+                mCache.getMetaData().reset();
+            }
+            synchronized (mCache) {
+                mCache.reset();
+            }
+            mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED);
         }
     }
 
-    private void updateRemoteViews(final int position, boolean notifyWhenLoaded) {
-        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
-
+    @WorkerThread
+    private void updateRemoteViews(IRemoteViewsFactory factory, int position,
+            boolean notifyWhenLoaded) {
         // Load the item information from the remote service
-        RemoteViews remoteViews = null;
-        long itemId = 0;
+        final RemoteViews remoteViews;
+        final long itemId;
         try {
             remoteViews = factory.getViewAt(position);
             itemId = factory.getItemId(position);
-        } catch (RemoteException e) {
+
+            if (remoteViews == null) {
+                throw new RuntimeException("Null remoteViews");
+            }
+        } catch (RemoteException | RuntimeException e) {
             Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
 
             // Return early to prevent additional work in re-centering the view cache, and
             // swapping from the loading view
             return;
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
-            return;
-        }
-
-        if (remoteViews == null) {
-            // If a null view was returned, we break early to prevent it from getting
-            // into our cache and causing problems later. The effect is that the child  at this
-            // position will remain as a loading view until it is updated.
-            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " +
-                    "returned from RemoteViewsFactory.");
-            return;
         }
 
         if (remoteViews.mApplication != null) {
@@ -1013,21 +1000,15 @@
         }
         synchronized (mCache) {
             if (viewTypeInRange) {
-                int[] visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
-                        mVisibleWindowUpperBound, cacheCount);
+                int[] visibleWindow = getVisibleWindow(cacheCount);
                 // Cache the RemoteViews we loaded
                 mCache.insert(position, remoteViews, itemId, visibleWindow);
 
-                // Notify all the views that we have previously returned for this index that
-                // there is new data for it.
-                final RemoteViews rv = remoteViews;
                 if (notifyWhenLoaded) {
-                    mMainQueue.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            mRequestedViews.notifyOnRemoteViewsLoaded(position, rv);
-                        }
-                    });
+                    // Notify all the views that we have previously returned for this index that
+                    // there is new data for it.
+                    Message.obtain(mMainHandler, MSG_MAIN_HANDLER_REMOTE_VIEWS_LOADED, position, 0,
+                            remoteViews).sendToTarget();
                 }
             } else {
                 // We need to log an error here, as the the view type count specified by the
@@ -1066,7 +1047,7 @@
     }
 
     public int getItemViewType(int position) {
-        int typeId = 0;
+        final int typeId;
         synchronized (mCache) {
             if (mCache.containsMetaDataAt(position)) {
                 typeId = mCache.getMetaDataAt(position).typeId;
@@ -1097,14 +1078,13 @@
         synchronized (mCache) {
             RemoteViews rv = mCache.getRemoteViewsAt(position);
             boolean isInCache = (rv != null);
-            boolean isConnected = mServiceConnection.isConnected();
             boolean hasNewItems = false;
 
             if (convertView != null && convertView instanceof RemoteViewsFrameLayout) {
                 mRequestedViews.removeView((RemoteViewsFrameLayout) convertView);
             }
 
-            if (!isInCache && !isConnected) {
+            if (!isInCache) {
                 // Requesting bind service will trigger a super.notifyDataSetChanged(), which will
                 // in turn trigger another request to getView()
                 requestBindService();
@@ -1124,7 +1104,9 @@
             if (isInCache) {
                 // Apply the view synchronously if possible, to avoid flickering
                 layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler, false);
-                if (hasNewItems) loadNextIndexInBackground();
+                if (hasNewItems) {
+                    mServiceHandler.sendEmptyMessage(MSG_LOAD_NEXT_ITEM);
+                }
             } else {
                 // If the views is not loaded, apply the loading view. If the loading view doesn't
                 // exist, the layout will create a default view based on the firstView height.
@@ -1134,7 +1116,7 @@
                         false);
                 mRequestedViews.add(position, layout);
                 mCache.queueRequestedPositionToLoad(position);
-                loadNextIndexInBackground();
+                mServiceHandler.sendEmptyMessage(MSG_LOAD_NEXT_ITEM);
             }
             return layout;
         }
@@ -1158,69 +1140,12 @@
         return getCount() <= 0;
     }
 
-    private void onNotifyDataSetChanged() {
-        // Complete the actual notifyDataSetChanged() call initiated earlier
-        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
-        try {
-            factory.onDataSetChanged();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
-
-            // Return early to prevent from further being notified (since nothing has
-            // changed)
-            return;
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
-            return;
-        }
-
-        // Flush the cache so that we can reload new items from the service
-        synchronized (mCache) {
-            mCache.reset();
-        }
-
-        // Re-request the new metadata (only after the notification to the factory)
-        updateTemporaryMetaData();
-        int newCount;
-        int[] visibleWindow;
-        synchronized(mCache.getTemporaryMetaData()) {
-            newCount = mCache.getTemporaryMetaData().count;
-            visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
-                    mVisibleWindowUpperBound, newCount);
-        }
-
-        // Pre-load (our best guess of) the views which are currently visible in the AdapterView.
-        // This mitigates flashing and flickering of loading views when a widget notifies that
-        // its data has changed.
-        for (int i: visibleWindow) {
-            // Because temporary meta data is only ever modified from this thread (ie.
-            // mWorkerThread), it is safe to assume that count is a valid representation.
-            if (i < newCount) {
-                updateRemoteViews(i, false);
-            }
-        }
-
-        // Propagate the notification back to the base adapter
-        mMainQueue.post(new Runnable() {
-            @Override
-            public void run() {
-                synchronized (mCache) {
-                    mCache.commitTemporaryMetaData();
-                }
-
-                superNotifyDataSetChanged();
-                enqueueDeferredUnbindServiceMessage();
-            }
-        });
-
-        // Reset the notify flagflag
-        mNotifyDataSetChangedAfterOnServiceConnected = false;
-    }
-
     /**
      * Returns a sorted array of all integers between lower and upper.
      */
-    private int[] getVisibleWindow(int lower, int upper, int count) {
+    private int[] getVisibleWindow(int count) {
+        int lower = mVisibleWindowLowerBound;
+        int upper = mVisibleWindowUpperBound;
         // In the case that the window is invalid or uninitialized, return an empty window.
         if ((lower == 0 && upper == 0) || lower < 0 || upper < 0) {
             return new int[0];
@@ -1250,23 +1175,8 @@
     }
 
     public void notifyDataSetChanged() {
-        // Dequeue any unbind messages
-        mMainQueue.removeMessages(sUnbindServiceMessageType);
-
-        // If we are not connected, queue up the notifyDataSetChanged to be handled when we do
-        // connect
-        if (!mServiceConnection.isConnected()) {
-            mNotifyDataSetChangedAfterOnServiceConnected = true;
-            requestBindService();
-            return;
-        }
-
-        mWorkerQueue.post(new Runnable() {
-            @Override
-            public void run() {
-                onNotifyDataSetChanged();
-            }
-        });
+        mServiceHandler.removeMessages(MSG_UNBIND_SERVICE);
+        mServiceHandler.sendEmptyMessage(MSG_NOTIFY_DATA_SET_CHANGED);
     }
 
     void superNotifyDataSetChanged() {
@@ -1275,35 +1185,38 @@
 
     @Override
     public boolean handleMessage(Message msg) {
-        boolean result = false;
         switch (msg.what) {
-        case sUnbindServiceMessageType:
-            if (mServiceConnection.isConnected()) {
-                mServiceConnection.unbind(mContext, mAppWidgetId, mIntent);
+            case MSG_MAIN_HANDLER_COMMIT_METADATA: {
+                mCache.commitTemporaryMetaData();
+                return true;
             }
-            result = true;
-            break;
-        default:
-            break;
+            case MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED: {
+                superNotifyDataSetChanged();
+                return true;
+            }
+            case MSG_MAIN_HANDLER_REMOTE_ADAPTER_CONNECTED: {
+                if (mCallback != null) {
+                    mCallback.onRemoteAdapterConnected();
+                }
+                return true;
+            }
+            case MSG_MAIN_HANDLER_REMOTE_ADAPTER_DISCONNECTED: {
+                if (mCallback != null) {
+                    mCallback.onRemoteAdapterDisconnected();
+                }
+                return true;
+            }
+            case MSG_MAIN_HANDLER_REMOTE_VIEWS_LOADED: {
+                mRequestedViews.notifyOnRemoteViewsLoaded(msg.arg1, (RemoteViews) msg.obj);
+                return true;
+            }
         }
-        return result;
+        return false;
     }
 
-    private void enqueueDeferredUnbindServiceMessage() {
-        // Remove any existing deferred-unbind messages
-        mMainQueue.removeMessages(sUnbindServiceMessageType);
-        mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay);
-    }
-
-    private boolean requestBindService() {
-        // Try binding the service (which will start it if it's not already running)
-        if (!mServiceConnection.isConnected()) {
-            mServiceConnection.bind(mContext, mAppWidgetId, mIntent);
-        }
-
-        // Remove any existing deferred-unbind messages
-        mMainQueue.removeMessages(sUnbindServiceMessageType);
-        return mServiceConnection.isConnected();
+    private void requestBindService() {
+        mServiceHandler.removeMessages(MSG_UNBIND_SERVICE);
+        Message.obtain(mServiceHandler, MSG_REQUEST_BIND, mAppWidgetId, 0, mIntent).sendToTarget();
     }
 
     private static class HandlerThreadExecutor implements Executor {
@@ -1331,7 +1244,7 @@
             remoteViews = views;
 
             float density = context.getResources().getDisplayMetrics().density;
-            defaultHeight = Math.round(sDefaultLoadingViewHeight * density);
+            defaultHeight = Math.round(DEFAULT_LOADING_VIEW_HEIGHT * density);
         }
 
         public void loadFirstViewHeight(
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ce80552..d9bc51f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9219,36 +9219,6 @@
         }
     }
 
-    @Override
-    public void invalidate() {
-        super.invalidate();
-
-        if (mEditor != null) {
-            mEditor.invalidateMagnifier();
-        }
-    }
-
-    @Override
-    public void invalidate(int l, int t, int r, int b) {
-        super.invalidate(l, t, r, b);
-
-        if (mEditor != null) {
-            mEditor.invalidateMagnifier(
-                    convertViewToScreenCoord(l, true /* isHorizontal */),
-                    convertViewToScreenCoord(t, false /* isHorizontal */),
-                    convertViewToScreenCoord(r, true /* isHorizontal */),
-                    convertViewToScreenCoord(b, false /* isHorizontal */));
-        }
-    }
-
-    float convertViewToScreenCoord(float viewCoord, boolean isHorizontal) {
-        final int[] coordinatesOnScreen = new int[2];
-        getLocationOnScreen(coordinatesOnScreen);
-        return isHorizontal
-                ? viewCoord + getTotalPaddingLeft() - getScrollX() + coordinatesOnScreen[0]
-                : viewCoord + getTotalPaddingTop() - getScrollY() + coordinatesOnScreen[1];
-    }
-
     /**
      * @return whether or not the cursor is visible (assuming this TextView is editable)
      *
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index caf35b3..a4da6b9c 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -26,6 +26,8 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.widget.RemoteViews;
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
 
 /** {@hide} */
 interface IAppWidgetService {
@@ -62,9 +64,9 @@
     void setBindAppWidgetPermission(in String packageName, int userId, in boolean permission);
     boolean bindAppWidgetId(in String callingPackage, int appWidgetId,
             int providerProfileId, in ComponentName providerComponent, in Bundle options);
-    void bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
-            in IBinder connection);
-    void unbindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent);
+    boolean bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
+            IApplicationThread caller, IBinder token, IServiceConnection connection, int flags);
+
     int[] getAppWidgetIds(in ComponentName providerComponent);
     boolean isBoundWidgetPackage(String packageName, int userId);
     boolean requestPinAppWidget(String packageName, in ComponentName providerComponent,
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2057292..9cc1959 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6483,6 +6483,11 @@
         }
 
         @Override
+        public Timer getWifiScanTimer() {
+            return mWifiScanTimer;
+        }
+
+        @Override
         public int getWifiScanBackgroundCount(int which) {
             if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) {
                 return 0;
@@ -6509,6 +6514,14 @@
         }
 
         @Override
+        public Timer getWifiScanBackgroundTimer() {
+            if (mWifiScanTimer == null) {
+                return null;
+            }
+            return mWifiScanTimer.getSubTimer();
+        }
+
+        @Override
         public long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which) {
             if (csphBin < 0 || csphBin >= NUM_WIFI_BATCHED_SCAN_BINS) return 0;
             if (mWifiBatchedScanTimer[csphBin] == null) {
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
index 46098c5..70b6f96 100644
--- a/core/java/com/android/internal/util/WakeupMessage.java
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -47,17 +47,19 @@
     protected final int mCmd, mArg1, mArg2;
     @VisibleForTesting
     protected final Object mObj;
+    private final Runnable mRunnable;
     private boolean mScheduled;
 
     public WakeupMessage(Context context, Handler handler,
             String cmdName, int cmd, int arg1, int arg2, Object obj) {
-        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        mAlarmManager = getAlarmManager(context);
         mHandler = handler;
         mCmdName = cmdName;
         mCmd = cmd;
         mArg1 = arg1;
         mArg2 = arg2;
         mObj = obj;
+        mRunnable = null;
     }
 
     public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) {
@@ -73,6 +75,21 @@
         this(context, handler, cmdName, cmd, 0, 0, null);
     }
 
+    public WakeupMessage(Context context, Handler handler, String cmdName, Runnable runnable) {
+        mAlarmManager = getAlarmManager(context);
+        mHandler = handler;
+        mCmdName = cmdName;
+        mCmd = 0;
+        mArg1 = 0;
+        mArg2 = 0;
+        mObj = null;
+        mRunnable = runnable;
+    }
+
+    private static AlarmManager getAlarmManager(Context context) {
+        return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+    }
+
     /**
      * Schedule the message to be delivered at the time in milliseconds of the
      * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} clock and wakeup
@@ -107,7 +124,12 @@
             mScheduled = false;
         }
         if (stillScheduled) {
-            Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
+            Message msg;
+            if (mRunnable == null) {
+                msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
+            } else {
+                msg = Message.obtain(mHandler, mRunnable);
+            }
             mHandler.dispatchMessage(msg);
             msg.recycle();
         }
diff --git a/core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl b/core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl
deleted file mode 100644
index 7294124..0000000
--- a/core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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 com.android.internal.widget;
-
-import android.os.IBinder;
-
-/** {@hide} */
-oneway interface IRemoteViewsAdapterConnection {
-    void onServiceConnected(IBinder service);
-    void onServiceDisconnected();
-}
diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java
index 9bc0778..6d54d7b 100644
--- a/core/java/com/android/internal/widget/Magnifier.java
+++ b/core/java/com/android/internal/widget/Magnifier.java
@@ -22,9 +22,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Handler;
 import android.util.Log;
 import android.view.Gravity;
@@ -38,13 +36,15 @@
 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";
-    // Use this to specify that a previous configuration value does not exist.
-    private static final int INEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+    private static final int MAGNIFIER_REFRESH_RATE_MS = 33; // ~30fps
     // The view for which this magnifier is attached.
     private final View mView;
     // The window containing the magnifier.
@@ -62,15 +62,10 @@
     // The callback of the pixel copy request will be invoked on this Handler when
     // the copy is finished.
     private final Handler mPixelCopyHandler = Handler.getMain();
-
-    private RectF mTmpRectF;
-
-    // Variables holding previous states, used for detecting redundant calls and invalidation.
-    private Point mPrevStartCoordsOnScreen = new Point(
-            INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE);
-    private PointF mPrevCenterCoordsOnScreen = new PointF(
-            INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE);
-    private float mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE;
+    // Current magnification scale.
+    private float mScale;
+    // Timer used to schedule the copy task.
+    private Timer mTimer;
 
     /**
      * Initializes a magnifier.
@@ -82,6 +77,7 @@
         mView = Preconditions.checkNotNull(view);
         final Context context = mView.getContext();
         final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null);
+        content.findViewById(R.id.magnifier_inner).setClipToOutline(true);
         mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width);
         mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height);
         final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation);
@@ -101,15 +97,15 @@
     /**
      * Shows the magnifier on the screen.
      *
-     * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source. The
-     *        lower end is clamped to 0
-     * @param centerYOnScreen vertical coordinate of the center point of the magnifier source. The
-     *        lower end is clamped to 0
+     * @param xPosInView horizontal coordinate of the center point of the magnifier source relative
+     *        to the view. The lower end is clamped to 0
+     * @param yPosInView vertical coordinate of the center point of the magnifier source
+     *        relative to the view. The lower end is clamped to 0
      * @param scale the scale at which the magnifier zooms on the source content. The
      *        lower end is clamped to 1 and the higher end to 4
      */
-    public void show(@FloatRange(from=0) float centerXOnScreen,
-            @FloatRange(from=0) float centerYOnScreen,
+    public void show(@FloatRange(from=0) float xPosInView,
+            @FloatRange(from=0) float yPosInView,
             @FloatRange(from=1, to=4) float scale) {
         if (scale > 4) {
             scale = 4;
@@ -119,27 +115,29 @@
             scale = 1;
         }
 
-        if (centerXOnScreen < 0) {
-            centerXOnScreen = 0;
+        if (xPosInView < 0) {
+            xPosInView = 0;
         }
 
-        if (centerYOnScreen < 0) {
-            centerYOnScreen = 0;
+        if (yPosInView < 0) {
+            yPosInView = 0;
         }
 
-        showInternal(centerXOnScreen, centerYOnScreen, scale, false);
-    }
-
-    private void showInternal(@FloatRange(from=0) float centerXOnScreen,
-            @FloatRange(from=0) float centerYOnScreen,
-            @FloatRange(from=1, to=4) float scale,
-            boolean forceShow) {
-        if (mPrevScale != scale) {
+        if (mScale != scale) {
             resizeBitmap(scale);
-            mPrevScale = scale;
         }
-        configureCoordinates(centerXOnScreen, centerYOnScreen);
-        maybePerformPixelCopy(scale, forceShow);
+        mScale = scale;
+        configureCoordinates(xPosInView, yPosInView);
+
+        if (mTimer == null) {
+            mTimer = new Timer();
+            mTimer.schedule(new TimerTask() {
+                @Override
+                public void run() {
+                    performPixelCopy();
+                }
+            }, 0 /* delay */, MAGNIFIER_REFRESH_RATE_MS);
+        }
 
         if (mWindow.isShowing()) {
             mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
@@ -148,9 +146,6 @@
             mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
                     mWindowCoords.x, mWindowCoords.y);
         }
-
-        mPrevCenterCoordsOnScreen.x = centerXOnScreen;
-        mPrevCenterCoordsOnScreen.y = centerYOnScreen;
     }
 
     /**
@@ -159,36 +154,10 @@
     public void dismiss() {
         mWindow.dismiss();
 
-        mPrevStartCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE;
-        mPrevStartCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE;
-        mPrevCenterCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE;
-        mPrevCenterCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE;
-        mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE;
-    }
-
-    /**
-     * Forces the magnifier to update content by taking and showing a new snapshot using the
-     * previous coordinates. It does this only if the magnifier is showing and the dirty rectangle
-     * intersects the rectangle which holds the content to be magnified.
-     *
-     * @param dirtyRectOnScreen the rectangle representing the screen bounds of the dirty region
-     */
-    public void invalidate(RectF dirtyRectOnScreen) {
-        if (mWindow.isShowing() && mPrevCenterCoordsOnScreen.x != INEXISTENT_PREVIOUS_CONFIG_VALUE
-                && mPrevCenterCoordsOnScreen.y != INEXISTENT_PREVIOUS_CONFIG_VALUE
-                && mPrevScale != INEXISTENT_PREVIOUS_CONFIG_VALUE) {
-            // Update the current showing RectF.
-            mTmpRectF = new RectF(mPrevStartCoordsOnScreen.x,
-                    mPrevStartCoordsOnScreen.y,
-                    mPrevStartCoordsOnScreen.x + mBitmap.getWidth(),
-                    mPrevStartCoordsOnScreen.y + mBitmap.getHeight());
-
-            // Update only if we are currently showing content that has been declared as invalid.
-            if (RectF.intersects(dirtyRectOnScreen, mTmpRectF)) {
-                // Update the contents shown in the magnifier.
-                showInternal(mPrevCenterCoordsOnScreen.x, mPrevCenterCoordsOnScreen.y, mPrevScale,
-                        true /* forceShow */);
-            }
+        if (mTimer != null) {
+            mTimer.cancel();
+            mTimer.purge();
+            mTimer = null;
         }
     }
 
@@ -213,7 +182,12 @@
         getImageView().setImageBitmap(mBitmap);
     }
 
-    private void configureCoordinates(float posXOnScreen, float posYOnScreen) {
+    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];
+
         mCenterZoomCoords.x = (int) posXOnScreen;
         mCenterZoomCoords.y = (int) posYOnScreen;
 
@@ -223,7 +197,7 @@
         mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
     }
 
-    private void maybePerformPixelCopy(final float scale, final boolean forceShow) {
+    private void performPixelCopy() {
         final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
         int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
 
@@ -234,13 +208,6 @@
             rawStartX = mView.getWidth() - mBitmap.getWidth();
         }
 
-        if (!forceShow && rawStartX == mPrevStartCoordsOnScreen.x
-                && startY == mPrevStartCoordsOnScreen.y
-                && scale == mPrevScale) {
-            // Skip, we are already showing the desired content.
-            return;
-        }
-
         final int startX = rawStartX;
         final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
 
@@ -251,11 +218,7 @@
                     new Rect(startX, startY, startX + mBitmap.getWidth(),
                             startY + mBitmap.getHeight()),
                     mBitmap,
-                    result -> {
-                        getImageView().invalidate();
-                        mPrevStartCoordsOnScreen.x = startX;
-                        mPrevStartCoordsOnScreen.y = startY;
-                    },
+                    result -> getImageView().invalidate(),
                     mPixelCopyHandler);
         } else {
             Log.d(LOG_TAG, "Could not perform PixelCopy request");
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index c6801bf..9e6985c 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -56,9 +56,9 @@
     if (langs != nullptr) {
         ScopedUtfChars str(env, langs);
         builder = new NativeFamilyBuilder(
-                minikin::FontStyle::registerLanguageList(str.c_str()), variant);
+                minikin::FontStyle::registerLocaleList(str.c_str()), variant);
     } else {
-        builder = new NativeFamilyBuilder(minikin::FontStyle::registerLanguageList(""), variant);
+        builder = new NativeFamilyBuilder(minikin::FontStyle::registerLocaleList(""), variant);
     }
     return reinterpret_cast<jlong>(builder);
 }
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 93bd16b2..fd62a19f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -544,7 +544,7 @@
     static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
         Paint* obj = reinterpret_cast<Paint*>(objHandle);
         ScopedUtfChars localesChars(env, locales);
-        jint minikinLangListId = minikin::FontStyle::registerLanguageList(localesChars.c_str());
+        jint minikinLangListId = minikin::FontStyle::registerLocaleList(localesChars.c_str());
         obj->setMinikinLangListId(minikinLangListId);
         return minikinLangListId;
     }
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index c3f9bf7..1efff7f 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -765,6 +765,14 @@
     if (startPos > totalRows) {
         ALOGE("startPos %d > actual rows %d", startPos, totalRows);
     }
+    if (totalRows > 0 && addedRows == 0) {
+        String8 msg;
+        msg.appendFormat("Row too big to fit into CursorWindow requiredPos=%d, totalRows=%d",
+                requiredPos, totalRows);
+        throw_sqlite3_exception(env, SQLITE_TOOBIG, NULL, msg.string());
+        return 0;
+    }
+
     jlong result = jlong(startPos) << 32 | jlong(totalRows);
     return result;
 }
diff --git a/core/proto/android/app/jobparameters.proto b/core/proto/android/app/jobparameters.proto
new file mode 100644
index 0000000..4f6a2a2
--- /dev/null
+++ b/core/proto/android/app/jobparameters.proto
@@ -0,0 +1,33 @@
+/*
+ * 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_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.JobParameters object.
+ */
+message JobParametersProto {
+    enum CancelReason {
+        REASON_CANCELLED = 0;
+        REASON_CONSTRAINTS_NOT_SATISFIED = 1;
+        REASON_PREEMPT = 2;
+        REASON_TIMEOUT = 3;
+        REASON_DEVICE_IDLE = 4;
+    }
+}
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 38879c0..c33c0a0 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -19,6 +19,8 @@
 
 package android.os;
 
+import "frameworks/base/core/proto/android/app/jobparameters.proto";
+import "frameworks/base/core/proto/android/os/powermanager.proto";
 import "frameworks/base/core/proto/android/telephony/signalstrength.proto";
 
 message BatteryStatsProto {
@@ -356,11 +358,12 @@
   optional PowerUseSummary power_use_summary = 18;
 
   message ResourcePowerManager {
+    // Either StateName or StateName.VoterName.
     optional string name = 1;
     optional TimerProto total = 2;
     optional TimerProto screen_off = 3;
   }
-  optional ResourcePowerManager resource_power_manager = 19;
+  repeated ResourcePowerManager resource_power_manager = 19;
 
   message ScreenBrightness {
     enum Name {
@@ -436,12 +439,332 @@
 }
 
 message TimerProto {
+  // This may be an apportioned time.
   optional int64 duration_ms = 1;
   optional int64 count = 2;
+  // The max duration if it is being tracked. Not all Timer subclasses
+  // track the max duration.
+  optional int64 max_duration_ms = 3;
+  // The current time the timer has been active, if it is being tracked.
+  // Not all Timer subclasses track the current duration.
+  optional int64 current_duration_ms = 4;
+  // The total cumulative duration (i.e. sum of past durations) that this timer
+  // has been on since reset. This may differ from duration_ms since, depending
+  // on the Timer, getTotalTimeLocked may represent the total 'blamed' or
+  // 'pooled' time, rather than the actual time. By contrast, total_duration_ms
+  // always gives the actual total time. Not all Timer subclasses track the
+  // total duration.
+  optional int64 total_duration_ms = 5;
 }
 
 message UidProto {
   // Combination of app ID and user ID.
   optional int32 uid = 1;
-  repeated string package_names = 2;
+
+  // The statistics associated with a particular package.
+  message Package {
+    optional string name = 1;
+
+    message Service {
+      optional string name = 1;
+      // Time spent started.
+      optional int64 start_duration_ms = 2;
+      optional int32 start_count = 3;
+      optional int32 launch_count = 4;
+    }
+    repeated Service services = 2;
+  }
+  repeated Package packages = 2;
+
+  optional ControllerActivityProto bluetooth_controller = 3;
+  optional ControllerActivityProto modem_controller = 4;
+  optional ControllerActivityProto wifi_controller = 5;
+
+  // Bluetooth misc data.
+  message BluetoothMisc {
+    // Duration spent BLE scanning blamed on this App (i.e. apportioned to this
+    // app amongst all apps doing BLE scanning; see explanation of 'apportioned'
+    // in App's comment).
+    optional TimerProto apportioned_ble_scan = 1;
+    // Background times aren't apportioned.
+    optional TimerProto background_ble_scan = 2;
+    // Running unoptimized BLE scanning, as defined by Bluetooth's
+    // AppScanStats.recordScanStart. As of May 2017, these are unfiltered,
+    // non-opportunistic, non-first-match scans. Durations are not
+    // pooled/apportioned.
+    optional TimerProto unoptimized_ble_scan = 3;
+    // Running unoptimized BLE scanning when app is in background. Durations are
+    // not pooled/apportioned.
+    optional TimerProto background_unoptimized_ble_scan = 4;
+    // Count of results returned by BLE scanning.
+    optional int32 ble_scan_result_count = 5;
+    // Count of results returned by BLE scans when app is in background.
+    // (Included in ble_scan_result_count.)
+    optional int32 background_ble_scan_result_count = 6;
+  }
+  optional BluetoothMisc bluetooth_misc = 6;
+
+  message Cpu {
+    // Total CPU time with processes executing in userspace. Summed up across
+    // multiple cores.
+    optional int64 user_duration_ms = 1;
+    // Total CPU time with processes executing kernel syscalls. Summed up across
+    // multiple cores.
+    optional int64 system_duration_ms = 2;
+
+    // CPU time broken down by CPU frequency (go/cpu-battery-metrics).
+    //
+    // These are real CPU time measurement from the kernel, so their sum can
+    // be different from the sum of user_duration_millis and
+    // system_duration_millis, which are just approximations. Data is not
+    // tracked when device is charging.
+    message ByFrequency {
+      // Index of the frequency in system.cpu_frequency. It starts from 1, to
+      // make it easier to analyze.
+      optional int32 frequency_index = 1;
+      // CPU time in milliseconds.
+      optional int64 total_duration_ms = 2;
+      // Screen-off CPU time in milliseconds.
+      optional int64 screen_off_duration_ms = 3;
+    }
+    repeated ByFrequency by_frequency = 3;
+  }
+  optional Cpu cpu = 7;
+
+  // Duration is pooled/apportioned.
+  optional TimerProto audio = 8;
+  // Duration is pooled/apportioned.
+  optional TimerProto camera = 9;
+  // Duration is pooled/apportioned.
+  optional TimerProto flashlight = 10;
+  // Duration is not pooled/apportioned.
+  optional TimerProto foreground_activity = 11;
+  // Duration is not pooled/apportioned.
+  optional TimerProto foreground_service = 12;
+  // Duration is not pooled/apportioned.
+  optional TimerProto vibrator = 13;
+  // Duration is pooled/apportioned.
+  optional TimerProto video = 14;
+
+  message Job {
+    optional string name = 1;
+    // Job times aren't apportioned.
+    optional TimerProto total = 2;
+    optional TimerProto background = 3;
+  }
+  repeated Job jobs = 15;
+
+  message JobCompletion {
+    // Job name.
+    optional string name = 1;
+
+    message ReasonCount {
+      optional android.app.JobParametersProto.CancelReason name = 1;
+      optional int32 count = 2;
+    }
+    repeated ReasonCount reason_count = 2;
+  };
+  repeated JobCompletion job_completion = 16;
+
+  message Network {
+    // Mobile data traffic (total, background + foreground).
+    optional int64 mobile_bytes_rx = 1;
+    optional int64 mobile_bytes_tx = 2;
+    // Wifi data traffic (total, background + foreground).
+    optional int64 wifi_bytes_rx = 3;
+    optional int64 wifi_bytes_tx = 4;
+    // Bluetooth data traffic (total, background + foreground).
+    optional int64 bt_bytes_rx = 5;
+    optional int64 bt_bytes_tx = 6;
+    // In packets (total, background + foreground).
+    optional int64 mobile_packets_rx = 7;
+    optional int64 mobile_packets_tx = 8;
+    optional int64 wifi_packets_rx = 9;
+    optional int64 wifi_packets_tx = 10;
+    // Radio active duration.
+    optional int64 mobile_active_duration_ms = 11;
+    optional int32 mobile_active_count = 12;
+    // Number of times the app woke up the mobile radio.
+    optional int32 mobile_wakeup_count = 13;
+    // Number of times the app woke up the wifi radio.
+    optional int32 wifi_wakeup_count = 14;
+    // Mobile data traffic in the background only, included in total above.
+    optional int64 mobile_bytes_bg_rx = 15;
+    optional int64 mobile_bytes_bg_tx = 16;
+    // Wifi data traffic in the background only, included in total above.
+    optional int64 wifi_bytes_bg_rx = 17;
+    optional int64 wifi_bytes_bg_tx = 18;
+    // In packets (background only, included in total packets above).
+    optional int64 mobile_packets_bg_rx = 19;
+    optional int64 mobile_packets_bg_tx = 20;
+    optional int64 wifi_packets_bg_rx = 21;
+    optional int64 wifi_packets_bg_tx = 22;
+  };
+  optional Network network = 17;
+
+  // TODO: combine System and App messages?
+  message PowerUseItem {
+    // Estimated power use in mAh.
+    optional double computed_power_mah = 1;
+    // Starting in Oreo, Battery Settings has two modes to display the battery
+    // info. The first is "app usage list". In this mode, items with should_hide
+    // enabled are hidden.
+    optional bool should_hide = 2;
+    // Smeared power from screen usage. Screen usage power is split and smeared
+    // among apps, based on activity time.
+    optional double screen_power_mah = 3;
+    // Smeared power using proportional method. Power usage from hidden sippers
+    // is smeared to all apps proportionally (except for screen usage).
+    optional double proportional_smear_mah = 4;
+  };
+  optional PowerUseItem power_use_item = 18;
+
+  // Durations are not pooled/apportioned.
+  message Process {
+    optional string name = 1;
+    // Time spent executing in user code.
+    optional int64 user_duration_ms = 2;
+    // Time spent executing in kernel code.
+    optional int64 system_duration_ms = 3;
+    // Time the process was running in the foreground.
+    optional int64 foreground_duration_ms = 4;
+    // Number of times the process has been started.
+    optional int32 start_count = 5;
+    // Number of times the process has had an ANR.
+    optional int32 anr_count = 6;
+    // Number of times the process has crashed.
+    optional int32 crash_count = 7;
+  };
+  repeated Process process = 19;
+
+  message StateTime {
+    // All of these (non-deprecated) states are mutually exclusive and can be
+    // added together to find the total time a uid has had any processes running
+    // at all.
+
+    // In approximate order or priority (top being what the framework considers
+    // most important and is thus least likely to kill when resources are
+    // needed:
+    // top > foreground service > top sleeping > foreground > background > cache
+    enum State {
+      // Time this uid has any processes in the top state (or above such as
+      // persistent).
+      PROCESS_STATE_TOP = 0;
+      // Time this uid has any process with a started out bound foreground
+      // service, but none in the "top" state.
+      PROCESS_STATE_FOREGROUND_SERVICE = 1;
+      // Time this uid has any process that is top while the device is sleeping,
+      // but none in the "foreground service" or better state. Sleeping is
+      // mostly screen off, but also includes the time when the screen is on but
+      // the device has not yet been unlocked.
+      PROCESS_STATE_TOP_SLEEPING = 2;
+      // Time this uid has any process in an active foreground state, but none
+      // in the "top sleeping" or better state.
+      PROCESS_STATE_FOREGROUND = 3;
+      // Time this uid has any process in an active background state, but none
+      // in the "foreground" or better state.
+      PROCESS_STATE_BACKGROUND = 4;
+      // Time this uid has any processes that are sitting around cached, not in
+      // one of the other active states.
+      PROCESS_STATE_CACHED = 5;
+    }
+    optional State state = 1;
+    optional int64 duration_ms = 2;
+  }
+  repeated StateTime states = 20;
+
+  message Sensor {
+    optional int32 id = 1;
+    optional TimerProto apportioned = 2;
+    // Background times aren't apportioned.
+    optional TimerProto background = 3;
+  }
+  repeated Sensor sensors = 21;
+
+  message Sync {
+    optional string name = 1;
+    // Sync times aren't apportioned.
+    optional TimerProto total = 2;
+    optional TimerProto background = 3;
+  }
+  repeated Sync syncs = 22;
+
+  message UserActivity {
+    optional android.os.PowerManagerProto.UserActivityEvent name = 1;
+    optional int32 count = 2;
+  };
+  repeated UserActivity user_activity = 23;
+
+  // Aggregated wakelock data for an app overall, across all of its wakelocks.
+  // The Wakelock message holds data about each *individual* wakelock, but it
+  // cannot be used to ascertain the aggregated time the app spent holding
+  // wakelocks, since merely summing Wakelock data will either underestimate (in
+  // the case of wakelock.partial.duration_ms) or overestimate (in the case of
+  // wakelock.partial.total_duration_ms) the total time, due to overlapping
+  // wakelocks. AggregatedWakelock, on the other hand, holds overall per-app
+  // wakelock data.
+  message AggregatedWakelock {
+    // The total duration that the app spent holding partial wakelocks.
+    // It includes both foreground + background use.
+    optional int64 partial_duration_ms = 1;
+    // The total duration that the app spent holding partial wakelocks while the
+    // app was in the background. Subtracting from partial_duration_ms will
+    // yield foreground usage.
+    optional int64 background_partial_duration_ms = 2;
+  };
+  optional AggregatedWakelock aggregated_wakelock = 24;
+
+  message Wakelock {
+    optional string name = 1;
+
+    // Full wakelocks keep the screen on. Based on
+    // PowerManager.SCREEN_BRIGHT_WAKE_LOCK (deprecated in API 13) and
+    // PowerManager.SCREEN_DIM_WAKE_LOCK (deprecated in API 17). Current, max,
+    // and total durations are not tracked for full wakelocks.
+    optional TimerProto full = 2;
+
+    // Partial wakelocks ensure the CPU is running while allowing the screen
+    // to turn off. Based on PowerManager.PARTIAL_WAKE_LOCK.
+    // Partial wakelock metrics are only recorded when the device is unplugged
+    // *and* the screen is off. Current, max, and total durations are tracked
+    // for partial wakelocks.
+    optional TimerProto partial = 3;
+
+    // These fields are for tracking partial wakelocks (see above), but only
+    // the time the wakelock was held while the app was in a background state.
+    // Since all background tracking is 'actual', not 'apportioned',
+    // background_partial.duration_ms is identical to
+    // background_partial.total_duration_ms.
+    optional TimerProto background_partial = 4;
+
+    // Window wakelocks keep the screen on. Current, max, and total durations
+    // are not tracked for window wakelocks.
+    optional TimerProto window = 5;
+  };
+  repeated Wakelock wakelocks = 25;
+
+  message WakeupAlarm {
+    // Wakeup alarm name.
+    optional string name = 1;
+    // Only includes counts when screen-off (& on battery).
+    optional int32 count = 2;
+  }
+  repeated WakeupAlarm wakeup_alarm = 26;
+
+  message Wifi {
+    // Duration holding Wifi-lock. This time is apportioned.
+    optional int64 full_wifi_lock_duration_ms = 1;
+    // Duration running Wifi. This time is apportioned.
+    optional int64 running_duration_ms = 2;
+    // Duration performing Wifi-scan blamed on this App (i.e. apportioned to
+    // this app amongst all apps doing Wifi-scanning; see explanation of
+    // 'apportioned' in App's comment).
+    optional TimerProto apportioned_scan = 3;
+    // Scans performed when app is in background. (Included in
+    // apportioned_scan). This value is not apportioned. Subtracting
+    // background_scan.total_duration_ms from apportioned_scan.total_duration_ms
+    // will yield foreground usage.
+    optional TimerProto background_scan = 4;
+  };
+  optional Wifi wifi = 27;
 }
diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto
new file mode 100644
index 0000000..3bfe5d6
--- /dev/null
+++ b/core/proto/android/os/powermanager.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+message PowerManagerProto {
+    /* User activity events in PowerManager.java. */
+    enum UserActivityEvent {
+        // Unspecified event type.
+        USER_ACTIVITY_EVENT_OTHER = 0;
+        // Button or key pressed or released.
+        USER_ACTIVITY_EVENT_BUTTON = 1;
+        // Touch down, move or up.
+        USER_ACTIVITY_EVENT_TOUCH = 2;
+        // Accessibility taking action on behalf of user.
+        USER_ACTIVITY_EVENT_ACCESSIBILITY = 3;
+    }
+}
diff --git a/core/res/res/layout/magnifier.xml b/core/res/res/layout/magnifier.xml
index 181e5e5..d6cd8b4 100644
--- a/core/res/res/layout/magnifier.xml
+++ b/core/res/res/layout/magnifier.xml
@@ -18,10 +18,17 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:background="?android:attr/floatingToolbarPopupBackgroundDrawable">
-    <ImageView
-        android:id="@+id/magnifier_image"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+    android:layout_height="wrap_content">
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/magnifier_inner"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"
+        android:elevation="@android:dimen/magnifier_elevation">
+        <ImageView
+            android:id="@+id/magnifier_image"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+    </LinearLayout>
 </LinearLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dec5fd9..571aad9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3128,4 +3128,7 @@
     <!-- Class names of device specific services inheriting com.android.server.SystemService. The
          classes are instantiated in the order of the array. -->
     <string-array translatable="false" name="config_deviceSpecificSystemServices"></string-array>
+
+    <!-- Component name of media projection permission dialog -->
+    <string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 14069e7..08e2233 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -521,7 +521,7 @@
     <dimen name="content_rect_bottom_clip_allowance">20dp</dimen>
 
     <!-- Magnifier dimensions -->
-    <dimen name="magnifier_width">200dp</dimen>
+    <dimen name="magnifier_width">164dp</dimen>
     <dimen name="magnifier_height">48dp</dimen>
     <dimen name="magnifier_elevation">2dp</dimen>
     <dimen name="magnifier_offset">42dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7bbd17e..1627a0e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2479,6 +2479,7 @@
 
   <!-- Magnifier -->
   <java-symbol type="id" name="magnifier_image" />
+  <java-symbol type="id" name="magnifier_inner" />
   <java-symbol type="layout" name="magnifier" />
   <java-symbol type="dimen" name="magnifier_width" />
   <java-symbol type="dimen" name="magnifier_height" />
@@ -3123,4 +3124,7 @@
   <java-symbol type="string" name="shortcut_restore_not_supported" />
   <java-symbol type="string" name="shortcut_restore_signature_mismatch" />
   <java-symbol type="string" name="shortcut_restore_unknown_issue" />
+
+  <!-- From media projection -->
+  <java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
 </resources>
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
new file mode 100644
index 0000000..06b860a
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -0,0 +1,380 @@
+/*
+ * 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.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IServiceConnection;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.database.DataSetObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.android.frameworks.coretests.R;
+import com.android.internal.widget.IRemoteViewsFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+/**
+ * Tests for RemoteViewsAdapter.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RemoteViewsAdapterTest {
+
+    @Mock AppWidgetManager mAppWidgetManager;
+    @Mock IServiceConnection mIServiceConnection;
+    @Mock RemoteViewsAdapter.RemoteAdapterConnectionCallback mCallback;
+
+    private Handler mMainHandler;
+    private TestContext mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mAppWidgetManager
+                .bindRemoteViewsService(any(), anyInt(), any(), any(), anyInt())).thenReturn(true);
+        mContext = new TestContext();
+        mMainHandler = new Handler(Looper.getMainLooper());
+    }
+
+    @Test
+    public void onRemoteAdapterConnected_after_metadata_loaded() throws Throwable {
+        RemoteViewsAdapter adapter = getOnUiThread(
+                () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
+        assertFalse(adapter.isDataReady());
+
+        assertNotNull(mContext.conn.get());
+
+        ViewsFactory factory = new ViewsFactory(1);
+        mContext.sendConnect(factory);
+
+        waitOnHandler(mMainHandler);
+        verify(mCallback, never()).onRemoteAdapterConnected();
+
+        factory.loadingView.set(createViews("loading"));
+        waitOnHandler(mContext.handler.get());
+        waitOnHandler(mMainHandler);
+        verify(mCallback, times(1)).onRemoteAdapterConnected();
+
+        assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
+
+        // Service is unbound
+        assertTrue(isUnboundOrScheduled());
+    }
+
+    @Test
+    public void viewReplaced_after_mainView_loaded() throws Throwable {
+        RemoteViewsAdapter adapter = getOnUiThread(
+                () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
+
+        ViewsFactory factory = new ViewsFactory(1);
+        factory.loadingView.set(createViews("loading"));
+        mContext.sendConnect(factory);
+
+        waitOnHandler(mContext.handler.get());
+        waitOnHandler(mMainHandler);
+
+        // Returned view contains the loading text
+        View view = getOnUiThread(() -> adapter.getView(0, null, new FrameLayout(mContext)));
+        ArrayList<View> search = new ArrayList<>();
+        view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
+        assertEquals(1, search.size());
+
+        // Send the final remoteViews
+        factory.views[0].set(createViews("updated"));
+        waitOnHandler(mContext.handler.get());
+        waitOnHandler(mMainHandler);
+
+        // Existing view got updated with new text
+        search.clear();
+        view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
+        assertTrue(search.isEmpty());
+        view.findViewsWithText(search, "updated", View.FIND_VIEWS_WITH_TEXT);
+        assertEquals(1, search.size());
+
+        // Service is unbound
+        assertTrue(isUnboundOrScheduled());
+    }
+
+    @Test
+    public void notifyDataSetChanged_deferred() throws Throwable {
+        RemoteViewsAdapter adapter = getOnUiThread(
+                () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
+
+        ViewsFactory factory = new ViewsFactory(1);
+        factory.loadingView.set(createViews("loading"));
+        mContext.sendConnect(factory);
+
+        waitOnHandler(mContext.handler.get());
+        waitOnHandler(mMainHandler);
+        assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
+
+        // Reset the loading view so that next refresh is blocked
+        factory.loadingView = new LockedValue<>();
+        factory.mCount = 3;
+        DataSetObserver observer = mock(DataSetObserver.class);
+        getOnUiThread(() -> {
+            adapter.registerDataSetObserver(observer);
+            adapter.notifyDataSetChanged();
+            return null;
+        });
+
+        waitOnHandler(mMainHandler);
+        // Still giving the old values
+        verify(observer, never()).onChanged();
+        assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
+
+        factory.loadingView.set(createViews("refreshed"));
+        waitOnHandler(mContext.handler.get());
+        waitOnHandler(mMainHandler);
+
+        // When the service returns new data, UI is updated.
+        verify(observer, times(1)).onChanged();
+        assertEquals((Integer) 3, getOnUiThread(adapter::getCount));
+
+        // Service is unbound
+        assertTrue(isUnboundOrScheduled());
+    }
+
+    @Test
+    public void serviceDisconnected_before_getView() throws Throwable {
+        RemoteViewsAdapter adapter = getOnUiThread(
+                () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
+
+        ViewsFactory factory = new ViewsFactory(1);
+        factory.loadingView.set(createViews("loading"));
+        mContext.sendConnect(factory);
+
+        waitOnHandler(mContext.handler.get());
+        waitOnHandler(mMainHandler);
+        verify(mCallback, times(1)).onRemoteAdapterConnected();
+        assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
+
+        // Unbind the service
+        ServiceConnection conn = mContext.conn.get();
+        getOnHandler(mContext.handler.get(), () -> {
+            conn.onServiceDisconnected(null);
+            return null;
+        });
+
+        // Returned view contains the loading text
+        View view = getOnUiThread(() -> adapter.getView(0, null, new FrameLayout(mContext)));
+        ArrayList<View> search = new ArrayList<>();
+        view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
+        assertEquals(1, search.size());
+
+        // Unbind is not scheduled
+        assertFalse(isUnboundOrScheduled());
+
+        mContext.sendConnect(factory);
+        waitOnHandler(mContext.handler.get());
+        waitOnHandler(mMainHandler);
+        verify(mCallback, times(2)).onRemoteAdapterConnected();
+    }
+
+    private RemoteViews createViews(String text) {
+        RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.remote_views_text);
+        views.setTextViewText(R.id.text, text);
+        return views;
+    }
+
+    private <T> T getOnUiThread(Supplier<T> supplier) throws Throwable {
+        return getOnHandler(mMainHandler, supplier);
+    }
+
+    private boolean isUnboundOrScheduled() throws Throwable {
+        Handler handler = mContext.handler.get();
+        return getOnHandler(handler, () -> mContext.boundCount == 0
+                || handler.hasMessages(RemoteViewsAdapter.MSG_UNBIND_SERVICE));
+    }
+
+    private static <T> T getOnHandler(Handler handler, Supplier<T> supplier) throws Throwable {
+        LockedValue<T> result = new LockedValue<>();
+        handler.post(() -> result.set(supplier.get()));
+        return result.get();
+    }
+
+    private class TestContext extends ContextWrapper {
+
+        public final LockedValue<ServiceConnection> conn = new LockedValue<>();
+        public final LockedValue<Handler> handler = new LockedValue<>();
+        public int boundCount;
+
+        TestContext() {
+            super(InstrumentationRegistry.getContext());
+        }
+
+        @Override
+        public void unbindService(ServiceConnection conn) {
+            boundCount--;
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (Context.APPWIDGET_SERVICE.equals(name)) {
+                return mAppWidgetManager;
+            }
+            return super.getSystemService(name);
+        }
+
+        @Override
+        public IServiceConnection getServiceDispatcher(
+                ServiceConnection conn, Handler handler, int flags) {
+            this.conn.set(conn);
+            this.handler.set(handler);
+            boundCount++;
+            return mIServiceConnection;
+        }
+
+        @Override
+        public Context getApplicationContext() {
+            return this;
+        }
+
+        public void sendConnect(ViewsFactory factory) throws Exception {
+            ServiceConnection connection = conn.get();
+            handler.get().post(() -> connection.onServiceConnected(null, factory.asBinder()));
+        }
+    }
+
+    private static void waitOnHandler(Handler handler) throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        handler.post(() -> latch.countDown());
+        latch.await(20, TimeUnit.SECONDS);
+    }
+
+    private static class ViewsFactory extends IRemoteViewsFactory.Stub {
+
+        public LockedValue<RemoteViews> loadingView = new LockedValue<>();
+        public LockedValue<RemoteViews>[] views;
+
+        private int mCount;
+
+        ViewsFactory(int count) {
+            mCount = count;
+            views = new LockedValue[count];
+            for (int i = 0; i < count; i++) {
+                views[i] = new LockedValue<>();
+            }
+        }
+
+        @Override
+        public void onDataSetChanged() {}
+
+        @Override
+        public void onDataSetChangedAsync() { }
+
+        @Override
+        public void onDestroy(Intent intent) { }
+
+        @Override
+        public int getCount() throws RemoteException {
+            return mCount;
+        }
+
+        @Override
+        public RemoteViews getViewAt(int position) throws RemoteException {
+            try {
+                return views[position].get();
+            } catch (Exception e) {
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        @Override
+        public RemoteViews getLoadingView() throws RemoteException {
+            try {
+                return loadingView.get();
+            } catch (Exception e) {
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 1;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return false;
+        }
+
+        @Override
+        public boolean isCreated() {
+            return false;
+        }
+    }
+
+    private static class DistinctIntent extends Intent {
+
+        @Override
+        public boolean filterEquals(Intent other) {
+            return false;
+        }
+    }
+
+    private static class LockedValue<T> {
+
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+        private T mValue;
+
+        public void set(T value) {
+            mValue = value;
+            mLatch.countDown();
+        }
+
+        public T get() throws Exception {
+            mLatch.await(10, TimeUnit.SECONDS);
+            return mValue;
+        }
+    }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
index 7935880..734ebef 100644
--- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
@@ -47,6 +47,7 @@
     private static final int TEST_ARG2 = 182;
     private static final Object TEST_OBJ = "hello";
 
+    @Mock Context mContext;
     @Mock AlarmManager mAlarmManager;
     WakeupMessage mMessage;
     // Make a spy so that we can verify calls to it
@@ -86,13 +87,12 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        Context context = mock(Context.class);
-        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+        when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
         // capture the listener for each AlarmManager.setExact call
         doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), any(String.class),
                 mListenerCaptor.capture(), any(Handler.class));
 
-        mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
+        mMessage = new WakeupMessage(mContext, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
                 TEST_ARG2, TEST_OBJ);
     }
 
@@ -168,4 +168,19 @@
         verifyMessageDispatchedOnce();
     }
 
+    /**
+     * Verify that a Runnable is scheduled and dispatched.
+     */
+    @Test
+    public void scheduleRunnable() {
+        final long when = 1011;
+        final Runnable runnable = mock(Runnable.class);
+        WakeupMessage dut = new WakeupMessage(mContext, mHandler, TEST_CMD_NAME, runnable);
+        dut.schedule(when);
+        verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when),
+                eq(TEST_CMD_NAME), any(AlarmManager.OnAlarmListener.class), eq(mHandler));
+        mListenerCaptor.getValue().onAlarm();
+        verify(runnable, times(1)).run();
+    }
+
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6f2b038..2f9ae57 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -24,6 +24,10 @@
         <permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" />
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.apps.tag">
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.backupconfirm">
         <permission name="android.permission.BACKUP"/>
         <permission name="android.permission.CRYPT_KEEPER"/>
@@ -54,6 +58,7 @@
         <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
         <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.STATUS_BAR"/>
         <permission name="android.permission.STOP_APP_SWITCHES"/>
         <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
         <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
@@ -353,6 +358,7 @@
         <permission name="android.permission.WRITE_DREAM_STATE"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.tv">
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 3b59bb1..588c4de 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -58,6 +58,7 @@
 
 bool Properties::filterOutTestOverhead = false;
 bool Properties::disableVsync = false;
+bool Properties::skpCaptureEnabled = false;
 
 static int property_get_int(const char* key, int defaultValue) {
     char buf[PROPERTY_VALUE_MAX] = {'\0',};
@@ -128,6 +129,9 @@
 
     filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false);
 
+    skpCaptureEnabled = property_get_bool("ro.debuggable", false)
+            && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false);
+
     return (prevDebugLayersUpdates != debugLayersUpdates)
             || (prevDebugOverdraw != debugOverdraw)
             || (prevDebugStencilClip != debugStencilClip);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 0fe4c77..077e7ae 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -165,6 +165,22 @@
  */
 #define PROPERTY_RENDERER "debug.hwui.renderer"
 
+/**
+ * Allows to collect a recording of Skia drawing commands.
+ */
+#define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled"
+
+
+/**
+ * Defines how many frames in a sequence to capture.
+ */
+#define PROPERTY_CAPTURE_SKP_FRAMES  "debug.hwui.capture_skp_frames"
+
+/**
+ * File name and location, where a SKP recording will be saved.
+ */
+#define PROPERTY_CAPTURE_SKP_FILENAME "debug.hwui.skp_filename"
+
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
 ///////////////////////////////////////////////////////////////////////////////
@@ -254,6 +270,8 @@
     // created after changing this.
     static bool disableVsync;
 
+    static bool skpCaptureEnabled;
+
     // Used for testing only to change the render pipeline.
 #ifdef HWUI_GLES_WRAP_ENABLED
     static void overrideRenderPipelineType(RenderPipelineType);
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 3b72a8b..def04e1 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -35,6 +35,10 @@
 }
 
 bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
+    if (context == nullptr) {
+        SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+        return false;
+    }
     // transform the matrix based on the layer
     SkMatrix layerTransform;
     layer->getTransform().copyTo(layerTransform);
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 47dee9d..ca93925 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -101,7 +101,7 @@
 
 void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
     RenderNode* renderNode = mRenderNode.get();
-    if (SkiaPipeline::skpCaptureEnabled()) {
+    if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
         SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
         canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index cdf4aee..a092cd5 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -112,7 +112,7 @@
 
             const Rect& layerDamage = layers.entries()[i].damage;
 
-            SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
+            SkCanvas* layerCanvas = tryCapture(layerNode->getLayerSurface());
 
             int saveCount = layerCanvas->save();
             SkASSERT(saveCount == 1);
@@ -139,9 +139,11 @@
             layerCanvas->restoreToCount(saveCount);
             mLightCenter = savedLightCenter;
 
+            endCapture(layerNode->getLayerSurface());
+
             // cache the current context so that we can defer flushing it until
             // either all the layers have been rendered or the context changes
-            GrContext* currentContext = layerCanvas->getGrContext();
+            GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
             if (cachedContext.get() != currentContext) {
                 if (cachedContext.get()) {
                     cachedContext->flush();
@@ -227,6 +229,91 @@
     }
 }
 
+class SkiaPipeline::SavePictureProcessor : public TaskProcessor<bool> {
+public:
+    explicit SavePictureProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
+
+    struct SavePictureTask : public Task<bool> {
+        sk_sp<SkData> data;
+        std::string filename;
+    };
+
+    void savePicture(const sk_sp<SkData>& data, const std::string& filename) {
+        sp<SavePictureTask> task(new SavePictureTask());
+        task->data = data;
+        task->filename = filename;
+        TaskProcessor<bool>::add(task);
+    }
+
+    virtual void onProcess(const sp<Task<bool> >& task) override {
+        SavePictureTask* t = static_cast<SavePictureTask*>(task.get());
+
+        if (0 == access(t->filename.c_str(), F_OK)) {
+            task->setResult(false);
+            return;
+        }
+
+        SkFILEWStream stream(t->filename.c_str());
+        if (stream.isValid()) {
+            stream.write(t->data->data(), t->data->size());
+            stream.flush();
+            SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s",
+                    stream.bytesWritten(), t->filename.c_str());
+        }
+
+        task->setResult(true);
+    }
+};
+
+SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) {
+    if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+        bool recordingPicture = mCaptureSequence > 0;
+        char prop[PROPERTY_VALUE_MAX] = {'\0'};
+        if (!recordingPicture) {
+            property_get(PROPERTY_CAPTURE_SKP_FILENAME, prop, "0");
+            recordingPicture = prop[0] != '0'
+                    && mCapturedFile != prop; //ensure we capture only once per filename
+            if (recordingPicture) {
+                mCapturedFile = prop;
+                mCaptureSequence = property_get_int32(PROPERTY_CAPTURE_SKP_FRAMES, 1);
+            }
+        }
+        if (recordingPicture) {
+            mRecorder.reset(new SkPictureRecorder());
+            return mRecorder->beginRecording(surface->width(), surface->height(), nullptr,
+                    SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
+        }
+    }
+    return surface->getCanvas();
+}
+
+void SkiaPipeline::endCapture(SkSurface* surface) {
+    if (CC_UNLIKELY(mRecorder.get())) {
+        sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
+        surface->getCanvas()->drawPicture(picture);
+        if (picture->approximateOpCount() > 0) {
+            SkDynamicMemoryWStream stream;
+            PngPixelSerializer serializer;
+            picture->serialize(&stream, &serializer);
+
+            //offload saving to file in a different thread
+            if (!mSavePictureProcessor.get()) {
+                TaskManager* taskManager = getTaskManager();
+                mSavePictureProcessor = new SavePictureProcessor(
+                        taskManager->canRunTasks() ? taskManager : nullptr);
+            }
+            if (1 == mCaptureSequence) {
+                mSavePictureProcessor->savePicture(stream.detachAsData(), mCapturedFile);
+            } else {
+                mSavePictureProcessor->savePicture(stream.detachAsData(),
+                                        mCapturedFile + "_" + std::to_string(mCaptureSequence));
+            }
+            mCaptureSequence--;
+        }
+        mRecorder.reset();
+    }
+}
+
 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
         const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
         const Rect &contentDrawBounds, sk_sp<SkSurface> surface) {
@@ -236,44 +323,21 @@
     // draw all layers up front
     renderLayersImpl(layers, opaque, wideColorGamut);
 
-    // initialize the canvas for the current frame
-    SkCanvas* canvas = surface->getCanvas();
-
+    // initialize the canvas for the current frame, that might be a recording canvas if SKP
+    // capture is enabled.
     std::unique_ptr<SkPictureRecorder> recorder;
-    bool recordingPicture = false;
-    char prop[PROPERTY_VALUE_MAX];
-    if (skpCaptureEnabled()) {
-        property_get("debug.hwui.capture_frame_as_skp", prop, "0");
-        recordingPicture = prop[0] != '0' && access(prop, F_OK) != 0;
-        if (recordingPicture) {
-            recorder.reset(new SkPictureRecorder());
-            canvas = recorder->beginRecording(surface->width(), surface->height(),
-                    nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
-        }
-    }
+    SkCanvas* canvas = tryCapture(surface.get());
 
     renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas);
 
-    if (skpCaptureEnabled() && recordingPicture) {
-        sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
-        if (picture->approximateOpCount() > 0) {
-            SkFILEWStream stream(prop);
-            if (stream.isValid()) {
-                PngPixelSerializer serializer;
-                picture->serialize(&stream, &serializer);
-                stream.flush();
-                SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
-            }
-        }
-        surface->getCanvas()->drawPicture(picture);
-    }
+    endCapture(surface.get());
 
     if (CC_UNLIKELY(Properties::debugOverdraw)) {
         renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
     }
 
     ATRACE_NAME("flush commands");
-    canvas->flush();
+    surface->getCanvas()->flush();
 }
 
 namespace {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index dc0b8f6d..67b1878 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -21,6 +21,8 @@
 #include "renderthread/IRenderPipeline.h"
 #include <SkSurface.h>
 
+class SkPictureRecorder;
+
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
@@ -56,9 +58,7 @@
 
     static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
 
-    static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
-
-    static bool skpCaptureEnabled() { return false; }
+    void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
 
     static float getLightRadius() {
         if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
@@ -127,12 +127,38 @@
      */
     void renderVectorDrawableCache();
 
+    SkCanvas* tryCapture(SkSurface* surface);
+    void endCapture(SkSurface* surface);
+
     std::vector<sk_sp<SkImage>> mPinnedImages;
 
     /**
      *  populated by prepareTree with dirty VDs
      */
     std::vector<VectorDrawableRoot*> mVectorDrawables;
+
+
+    // Block of properties used only for debugging to record a SkPicture and save it in a file.
+    /**
+     * mCapturedFile is used to enforce we don't capture more than once for a given name (cause
+     * permissions don't allow to reset a property from render thread).
+     */
+    std::string mCapturedFile;
+    /**
+     *  mCaptureSequence counts how many frames are left to take in the sequence.
+     */
+    int mCaptureSequence = 0;
+    /**
+     *  mSavePictureProcessor is used to run the file saving code in a separate thread.
+     */
+    class SavePictureProcessor;
+    sp<SavePictureProcessor> mSavePictureProcessor;
+    /**
+     *  mRecorder holds the current picture recorder. We could store it on the stack to support
+     *  parallel tryCapture calls (not really needed).
+     */
+    std::unique_ptr<SkPictureRecorder> mRecorder;
+
     static float mLightRadius;
     static uint8_t mAmbientShadowAlpha;
     static uint8_t mSpotShadowAlpha;
diff --git a/libs/hwui/tests/scripts/skp-capture.sh b/libs/hwui/tests/scripts/skp-capture.sh
new file mode 100755
index 0000000..54fa229
--- /dev/null
+++ b/libs/hwui/tests/scripts/skp-capture.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if [ -z "$1" ]; then
+    printf 'Usage:\n    skp-capture.sh PACKAGE_NAME OPTIONAL_FRAME_COUNT\n\n'
+    printf "Use \`adb shell 'pm list packages'\` to get a listing.\n\n"
+    exit 1
+fi
+if ! command -v adb > /dev/null 2>&1; then
+    if [ -x "${ANDROID_SDK_ROOT}/platform-tools/adb" ]; then
+        adb() {
+            "${ANDROID_SDK_ROOT}/platform-tools/adb" "$@"
+        }
+    else
+        echo 'adb missing'
+        exit 2
+    fi
+fi
+phase1_timeout_seconds=15
+phase2_timeout_seconds=60
+package="$1"
+filename="$(date '+%H%M%S').skp"
+remote_path="/data/data/${package}/cache/${filename}"
+local_path_prefix="$(date '+%Y-%m-%d_%H%M%S')_${package}"
+local_path="${local_path_prefix}.skp"
+enable_capture_key='debug.hwui.capture_skp_enabled'
+enable_capture_value=$(adb shell "getprop '${enable_capture_key}'")
+#printf 'captureflag=' "$enable_capture_value" '\n'
+if [ -z "$enable_capture_value" ]; then
+    printf 'Capture SKP property need to be enabled first. Please use\n'
+    printf "\"adb shell setprop debug.hwui.capture_skp_enabled true\" and then restart\n"
+    printf "the process.\n\n"
+    exit 1
+fi
+if [ ! -z "$2" ]; then
+    adb shell "setprop 'debug.hwui.capture_skp_frames' $2"
+fi
+filename_key='debug.hwui.skp_filename'
+adb shell "setprop '${filename_key}' '${remote_path}'"
+spin() {
+    case "$spin" in
+         1) printf '\b|';;
+         2) printf '\b\\';;
+         3) printf '\b-';;
+         *) printf '\b/';;
+    esac
+    spin=$(( ( ${spin:-0} + 1 ) % 4 ))
+    sleep $1
+}
+
+banner() {
+    printf '\n=====================\n'
+    printf '   %s' "$*"
+    printf '\n=====================\n'
+}
+banner '...WAITING...'
+adb_test_exist() {
+    test '0' = "$(adb shell "test -e \"$1\"; echo \$?")";
+}
+timeout=$(( $(date +%s) + $phase1_timeout_seconds))
+while ! adb_test_exist "$remote_path"; do
+    spin 0.05
+    if [ $(date +%s) -gt $timeout ] ; then
+        printf '\bTimed out.\n'
+        adb shell "setprop '${filename_key}' ''"
+        exit 3
+    fi
+done
+printf '\b'
+
+#read -n1 -r -p "Press any key to continue..." key
+
+banner '...SAVING...'
+adb_test_file_nonzero() {
+    # grab first byte of `du` output
+    X="$(adb shell "du \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")"
+    test "$X" && test "$X" -ne 0
+}
+#adb_filesize() {
+#    adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}'
+#}
+timeout=$(( $(date +%s) + $phase2_timeout_seconds))
+while ! adb_test_file_nonzero "$remote_path"; do
+    spin 0.05
+    if [ $(date +%s) -gt $timeout ] ; then
+        printf '\bTimed out.\n'
+        adb shell "setprop '${filename_key}' ''"
+        exit 3
+    fi
+done
+printf '\b'
+
+adb shell "setprop '${filename_key}' ''"
+
+i=0; while [ $i -lt 10 ]; do spin 0.10; i=$(($i + 1)); done; echo
+
+adb pull "$remote_path" "$local_path"
+if ! [ -f "$local_path" ] ; then
+    printf "something went wrong with `adb pull`."
+    exit 4
+fi
+adb shell rm "$remote_path"
+printf '\nSKP saved to file:\n    %s\n\n'  "$local_path"
+if [ ! -z "$2" ]; then
+    bridge="_"
+    adb shell "setprop 'debug.hwui.capture_skp_frames' ''"
+    for i in $(seq 2 $2); do
+        adb pull "${remote_path}_${i}" "${local_path_prefix}_${i}.skp"
+        adb shell rm "${remote_path}_${i}"
+    done
+fi
+
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 4c3e182..d182d78 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -22,6 +22,7 @@
 #include "IContextFactory.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaPipeline.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/SkiaRecordingCanvas.h"
 #include "renderthread/CanvasContext.h"
 #include "tests/common/TestUtils.h"
@@ -335,7 +336,7 @@
     EXPECT_EQ(3, canvas.getIndex());
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
     /* R is backward projected on B and C is a layer.
                 A
                / \
@@ -450,7 +451,8 @@
     LayerUpdateQueue layerUpdateQueue;
     layerUpdateQueue.enqueueLayerWithDamage(child.get(),
             android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
-    SkiaPipeline::renderLayersImpl(layerUpdateQueue, true, false);
+    auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+    pipeline->renderLayersImpl(layerUpdateQueue, true, false);
     EXPECT_EQ(1, drawCounter);  //assert index 0 is drawn on the layer
 
     RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
diff --git a/libs/services/Android.mk b/libs/services/Android.mk
index cbfd4b3..d72059a 100644
--- a/libs/services/Android.mk
+++ b/libs/services/Android.mk
@@ -21,7 +21,8 @@
 LOCAL_MODULE := libservices
 LOCAL_SRC_FILES := \
     ../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
-    src/os/DropBoxManager.cpp
+    src/os/DropBoxManager.cpp \
+    src/os/StatsLogEventWrapper.cpp
 
 LOCAL_AIDL_INCLUDES := \
     $(LOCAL_PATH)/../../core/java
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
new file mode 100644
index 0000000..255619c
--- /dev/null
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef STATS_LOG_EVENT_WRAPPER_H
+#define STATS_LOG_EVENT_WRAPPER_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/RefBase.h>
+#include <vector>
+
+namespace android {
+namespace os {
+
+// Represents a parcelable object. Only used to send data from Android OS to statsd.
+class StatsLogEventWrapper : public android::Parcelable {
+ public:
+  StatsLogEventWrapper();
+
+  StatsLogEventWrapper(StatsLogEventWrapper&& in) = default;
+
+  android::status_t writeToParcel(android::Parcel* out) const;
+
+  android::status_t readFromParcel(const android::Parcel* in);
+
+  // These are public for ease of conversion.
+  std::vector<uint8_t> bytes;
+};
+} // Namespace os
+} // Namespace android
+
+
+#endif  // STATS_LOG_EVENT_WRAPPER_H
+
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
new file mode 100644
index 0000000..8b3aa9a
--- /dev/null
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -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.
+ */
+#include <android/os/StatsLogEventWrapper.h>
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/RefBase.h>
+#include <vector>
+
+using android::Parcel;
+using android::Parcelable;
+using android::status_t;
+using std::vector;
+
+namespace android {
+namespace os {
+
+StatsLogEventWrapper::StatsLogEventWrapper(){};
+
+status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
+    out->writeByteVector(bytes);
+    return ::android::NO_ERROR;
+};
+
+status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
+    in->readByteVector(&bytes);
+    return ::android::NO_ERROR;
+};
+
+} // Namespace os
+} // Namespace android
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 9f2c08e..aa0d0cc 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -20,8 +20,10 @@
 import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.app.Activity;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.media.projection.IMediaProjection;
 import android.os.Handler;
 import android.os.IBinder;
@@ -71,8 +73,11 @@
      */
     public Intent createScreenCaptureIntent() {
         Intent i = new Intent();
-        i.setClassName("com.android.systemui",
-                "com.android.systemui.media.MediaProjectionPermissionActivity");
+        final ComponentName mediaProjectionPermissionDialogComponent =
+                ComponentName.unflattenFromString(mContext.getResources().getString(
+                        com.android.internal.R.string
+                        .config_mediaProjectionPermissionDialogComponent));
+        i.setComponent(mediaProjectionPermissionDialogComponent);
         return i;
     }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 51afada..76e7782 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -21,9 +21,12 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManagerInternal;
@@ -99,7 +102,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.widget.IRemoteViewsAdapterConnection;
 import com.android.internal.widget.IRemoteViewsFactory;
 import com.android.server.LocalServices;
 import com.android.server.WidgetBackupProvider;
@@ -191,10 +193,6 @@
         }
     };
 
-    // Manages active connections to RemoteViewsServices.
-    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
-            mBoundRemoteViewsServices = new HashMap<>();
-
     // Manages persistent references to RemoteViewsServices from different App Widgets.
     private final HashMap<Pair<Integer, FilterComparison>, HashSet<Integer>>
             mRemoteViewsServicesAppWidgets = new HashMap<>();
@@ -1209,17 +1207,14 @@
     }
 
     @Override
-    public void bindRemoteViewsService(String callingPackage, int appWidgetId,
-            Intent intent, IBinder callbacks) {
+    public boolean bindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent,
+            IApplicationThread caller, IBinder activtiyToken, IServiceConnection connection,
+            int flags) {
         final int userId = UserHandle.getCallingUserId();
-
         if (DEBUG) {
             Slog.i(TAG, "bindRemoteViewsService() " + userId);
         }
 
-        // Make sure the package runs under the caller uid.
-        mSecurityPolicy.enforceCallFromPackage(callingPackage);
-
         synchronized (mLock) {
             ensureGroupStateLoadedLocked(userId);
 
@@ -1254,76 +1249,35 @@
             mSecurityPolicy.enforceServiceExistsAndRequiresBindRemoteViewsPermission(
                     componentName, widget.provider.getUserId());
 
-            // Good to go - the service pakcage is correct, it exists for the correct
+            // Good to go - the service package is correct, it exists for the correct
             // user, and requires the bind permission.
 
-            // If there is already a connection made for this service intent, then
-            // disconnect from that first. (This does not allow multiple connections
-            // to the same service under the same key).
-            ServiceConnectionProxy connection = null;
-            FilterComparison fc = new FilterComparison(intent);
-            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                // Ask ActivityManager to bind it. Notice that we are binding the service with the
+                // caller app instead of DevicePolicyManagerService.
+                if(ActivityManager.getService().bindService(
+                        caller, activtiyToken, intent,
+                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                        connection, flags, mContext.getOpPackageName(),
+                        widget.provider.getUserId()) != 0) {
 
-            if (mBoundRemoteViewsServices.containsKey(key)) {
-                connection = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
-                connection.disconnect();
-                unbindService(connection);
-                mBoundRemoteViewsServices.remove(key);
-            }
-
-            // Bind to the RemoteViewsService (which will trigger a callback to the
-            // RemoteViewsAdapter.onServiceConnected())
-            connection = new ServiceConnectionProxy(callbacks);
-            bindService(intent, connection, widget.provider.info.getProfile());
-            mBoundRemoteViewsServices.put(key, connection);
-
-            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we
-            // can determine when we can call back to the RemoteViewsService later to
-            // destroy associated factories.
-            Pair<Integer, FilterComparison> serviceId = Pair.create(widget.provider.id.uid, fc);
-            incrementAppWidgetServiceRefCount(appWidgetId, serviceId);
-        }
-    }
-
-    @Override
-    public void unbindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent) {
-        final int userId = UserHandle.getCallingUserId();
-
-        if (DEBUG) {
-            Slog.i(TAG, "unbindRemoteViewsService() " + userId);
-        }
-
-        // Make sure the package runs under the caller uid.
-        mSecurityPolicy.enforceCallFromPackage(callingPackage);
-
-        synchronized (mLock) {
-            ensureGroupStateLoadedLocked(userId);
-
-            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
-            // RemoteViewsAdapter)
-            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
-                    new FilterComparison(intent));
-            if (mBoundRemoteViewsServices.containsKey(key)) {
-                // We don't need to use the appWidgetId until after we are sure there is something
-                // to unbind. Note that this may mask certain issues with apps calling unbind()
-                // more than necessary.
-
-                // NOTE: The lookup is enforcing security across users by making
-                // sure the caller can only access widgets it hosts or provides.
-                Widget widget = lookupWidgetLocked(appWidgetId,
-                        Binder.getCallingUid(), callingPackage);
-
-                if (widget == null) {
-                    throw new IllegalArgumentException("Bad widget id " + appWidgetId);
+                    // Add it to the mapping of RemoteViewsService to appWidgetIds so that we
+                    // can determine when we can call back to the RemoteViewsService later to
+                    // destroy associated factories.
+                    incrementAppWidgetServiceRefCount(appWidgetId,
+                            Pair.create(widget.provider.id.uid, new FilterComparison(intent)));
+                    return true;
                 }
-
-                ServiceConnectionProxy connection = (ServiceConnectionProxy)
-                        mBoundRemoteViewsServices.get(key);
-                connection.disconnect();
-                mContext.unbindService(connection);
-                mBoundRemoteViewsServices.remove(key);
+            } catch (RemoteException ex) {
+                // Same process, should not happen.
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
             }
         }
+
+        // Failed to bind.
+        return false;
     }
 
     @Override
@@ -1754,7 +1708,9 @@
 
     private void deleteAppWidgetLocked(Widget widget) {
         // We first unbind all services that are bound to this id
-        unbindAppWidgetRemoteViewsServicesLocked(widget);
+        // Check if we need to destroy any services (if no other app widgets are
+        // referencing the same service)
+        decrementAppWidgetServiceRefCount(widget);
 
         Host host = widget.host;
         host.widgets.remove(widget);
@@ -1796,28 +1752,6 @@
         }
     }
 
-    // Unbinds from a RemoteViewsService when we delete an app widget
-    private void unbindAppWidgetRemoteViewsServicesLocked(Widget widget) {
-        int appWidgetId = widget.appWidgetId;
-        // Unbind all connections to Services bound to this AppWidgetId
-        Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
-                .iterator();
-        while (it.hasNext()) {
-            final Pair<Integer, Intent.FilterComparison> key = it.next();
-            if (key.first == appWidgetId) {
-                final ServiceConnectionProxy conn = (ServiceConnectionProxy)
-                        mBoundRemoteViewsServices.get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
-                it.remove();
-            }
-        }
-
-        // Check if we need to destroy any services (if no other app widgets are
-        // referencing the same service)
-        decrementAppWidgetServiceRefCount(widget);
-    }
-
     // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
     private void destroyRemoteViewsService(final Intent intent, Widget widget) {
         final ServiceConnection conn = new ServiceConnection() {
@@ -1853,7 +1787,7 @@
     // Adds to the ref-count for a given RemoteViewsService intent
     private void incrementAppWidgetServiceRefCount(int appWidgetId,
             Pair<Integer, FilterComparison> serviceId) {
-        HashSet<Integer> appWidgetIds = null;
+        final HashSet<Integer> appWidgetIds;
         if (mRemoteViewsServicesAppWidgets.containsKey(serviceId)) {
             appWidgetIds = mRemoteViewsServicesAppWidgets.get(serviceId);
         } else {
@@ -4055,40 +3989,6 @@
         }
     }
 
-    /**
-     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
-     * needs to be a static inner class since a reference to the ServiceConnection is held globally
-     * and may lead us to leak AppWidgetService instances (if there were more than one).
-     */
-    private static final class ServiceConnectionProxy implements ServiceConnection {
-        private final IRemoteViewsAdapterConnection mConnectionCb;
-
-        ServiceConnectionProxy(IBinder connectionCb) {
-            mConnectionCb = IRemoteViewsAdapterConnection.Stub
-                    .asInterface(connectionCb);
-        }
-
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            try {
-                mConnectionCb.onServiceConnected(service);
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Error passing service interface", re);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName name) {
-            disconnect();
-        }
-
-        public void disconnect() {
-            try {
-                mConnectionCb.onServiceDisconnected();
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Error clearing service interface", re);
-            }
-        }
-    }
-
     private class LoadedWidgetState {
         final Widget widget;
         final int hostTag;
@@ -4642,7 +4542,9 @@
                         // reconstructed due to the restore
                         host.widgets.remove(widget);
                         provider.widgets.remove(widget);
-                        unbindAppWidgetRemoteViewsServicesLocked(widget);
+                        // Check if we need to destroy any services (if no other app widgets are
+                        // referencing the same service)
+                        decrementAppWidgetServiceRefCount(widget);
                         removeWidgetLocked(widget);
                     }
                 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 59fc34d..b720f74 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -846,12 +846,9 @@
      * when necessary.
      */
     public void logContextCommittedLocked() {
-        if (mResponses == null) {
-            if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): skipped (no responses)");
-            return;
-        }
+        final FillResponse lastResponse = getLastResponseLocked("logContextCommited()");
+        if (lastResponse == null) return;
 
-        final FillResponse lastResponse = mResponses.valueAt(mResponses.size() -1);
         final int flags = lastResponse.getFlags();
         if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) {
             if (sDebug) Slog.d(TAG, "logContextCommittedLocked(): ignored by flags " + flags);
@@ -1599,11 +1596,10 @@
      * Checks whether a view should be ignored.
      */
     private boolean isIgnoredLocked(AutofillId id) {
-        if (mResponses == null || mResponses.size() == 0) {
-            return false;
-        }
         // Always check the latest response only
-        final FillResponse response = mResponses.valueAt(mResponses.size() - 1);
+        final FillResponse response = getLastResponseLocked(null);
+        if (response == null) return false;
+
         return ArrayUtils.contains(response.getIgnoredIds(), id);
     }
 
@@ -1680,13 +1676,10 @@
     }
 
     private void updateTrackedIdsLocked() {
-        if (mResponses == null || mResponses.size() == 0) {
-            return;
-        }
-
         // Only track the views of the last response as only those are reported back to the
         // service, see #showSaveLocked
-        final FillResponse response = mResponses.valueAt(getLastResponseIndexLocked());
+        final FillResponse response = getLastResponseLocked(null);
+        if (response == null) return;
 
         ArraySet<AutofillId> trackedViews = null;
         boolean saveOnAllViewsInvisible = false;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 736a766..c15b5e2 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3005,12 +3005,7 @@
 
     @Override
     public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
-        if (wasTrimmed) {
-            // Task was trimmed from the recent tasks list -- remove the active task record as well
-            // since the user won't really be able to go back to it
-            removeTaskByIdLocked(task.taskId, false /* killProcess */,
-                    false /* removeFromRecents */);
-        }
+        // TODO: Trim active task once b/68045330 is fixed
         task.removedFromRecents();
     }
 
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
index 5d6e9b5..1dcb0ad 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -17,8 +17,6 @@
 package com.android.server.am;
 
 import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
 import android.os.SystemClock;
 import android.util.Slog;
 import android.view.WindowManager;
@@ -27,21 +25,19 @@
 import com.android.internal.R;
 
 /**
- *  Helper to manage showing/hiding a image to notify them that they are entering
- *  or exiting screen pinning mode.
+ *  Helper to manage showing/hiding a image to notify them that they are entering or exiting screen
+ *  pinning mode. All exposed methods should be called from a handler thread.
  */
 public class LockTaskNotify {
     private static final String TAG = "LockTaskNotify";
     private static final long SHOW_TOAST_MINIMUM_INTERVAL = 1000;
 
     private final Context mContext;
-    private final H mHandler;
     private Toast mLastToast;
     private long mLastShowToastTime;
 
     public LockTaskNotify(Context context) {
         mContext = context;
-        mHandler = new H();
     }
 
     /** Show "Screen pinned" toast. */
@@ -56,10 +52,6 @@
 
     /** Show a toast that describes the gesture the user should use to escape pinned mode. */
     void showEscapeToast() {
-        mHandler.obtainMessage(H.SHOW_ESCAPE_TOAST).sendToTarget();
-    }
-
-    private void handleShowEscapeToast() {
         long showToastTime = SystemClock.elapsedRealtime();
         if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) {
             Slog.i(TAG, "Ignore toast since it is requested in very short interval.");
@@ -79,17 +71,4 @@
         toast.show();
         return toast;
     }
-
-    private final class H extends Handler {
-        private static final int SHOW_ESCAPE_TOAST = 3;
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch(msg.what) {
-                case SHOW_ESCAPE_TOAST:
-                    handleShowEscapeToast();
-                    break;
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 515fa39..755c5f0 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -34,8 +34,6 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.media.AudioAttributes;
-import android.nfc.INfcAdapter;
-import android.nfc.NfcAdapter;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -124,7 +122,6 @@
     private static String METRIC_RADIOS = "shutdown_radios";
     private static String METRIC_BT = "shutdown_bt";
     private static String METRIC_RADIO = "shutdown_radio";
-    private static String METRIC_NFC = "shutdown_nfc";
     private static String METRIC_SM = "shutdown_storage_manager";
 
     private final Object mActionDoneSync = new Object();
@@ -629,29 +626,14 @@
         Thread t = new Thread() {
             public void run() {
                 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
-                boolean nfcOff;
                 boolean bluetoothReadyForShutdown;
                 boolean radioOff;
 
-                final INfcAdapter nfc =
-                        INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
                 final ITelephony phone =
                         ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
                 final IBluetoothManager bluetooth =
                         IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
                                 BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
-                try {
-                    nfcOff = nfc == null ||
-                             nfc.getState() == NfcAdapter.STATE_OFF;
-                    if (!nfcOff) {
-                        Log.w(TAG, "Turning off NFC...");
-                        metricStarted(METRIC_NFC);
-                        nfc.disable(false); // Don't persist new state
-                    }
-                } catch (RemoteException ex) {
-                Log.e(TAG, "RemoteException during NFC shutdown", ex);
-                    nfcOff = true;
-                }
 
                 try {
                     bluetoothReadyForShutdown = bluetooth == null ||
@@ -678,7 +660,7 @@
                     radioOff = true;
                 }
 
-                Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
+                Log.i(TAG, "Waiting for Bluetooth and Radio...");
 
                 long delay = endTime - SystemClock.elapsedRealtime();
                 while (delay > 0) {
@@ -722,23 +704,9 @@
                                     .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
                         }
                     }
-                    if (!nfcOff) {
-                        try {
-                            nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
-                        } catch (RemoteException ex) {
-                            Log.e(TAG, "RemoteException during NFC shutdown", ex);
-                            nfcOff = true;
-                        }
-                        if (nfcOff) {
-                            Log.i(TAG, "NFC turned off.");
-                            metricEnded(METRIC_NFC);
-                            shutdownTimingsTraceLog
-                                    .logDuration("ShutdownNfc", TRON_METRICS.get(METRIC_NFC));
-                        }
-                    }
 
-                    if (radioOff && bluetoothReadyForShutdown && nfcOff) {
-                        Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
+                    if (radioOff && bluetoothReadyForShutdown) {
+                        Log.i(TAG, "Radio and Bluetooth shutdown complete.");
                         done[0] = true;
                         break;
                     }
@@ -755,7 +723,7 @@
         } catch (InterruptedException ex) {
         }
         if (!done[0]) {
-            Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
+            Log.w(TAG, "Timed out waiting for Radio and Bluetooth shutdown.");
         }
     }
 
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index ca3dd05..22d2bcf 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -33,12 +33,14 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StatsLogEventWrapper;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
 
 import java.util.ArrayList;
 import java.util.List;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
@@ -49,6 +51,7 @@
 /**
  * Helper service for statsd (the native stats management service in cmds/statsd/).
  * Used for registering and receiving alarms on behalf of statsd.
+ *
  * @hide
  */
 public class StatsCompanionService extends IStatsCompanionService.Stub {
@@ -90,7 +93,7 @@
                         // Needed since the new user basically has a version of every app.
                         informAllUidsLocked(context);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+                        Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
                         forgetEverything();
                     }
                 }
@@ -99,9 +102,9 @@
         Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
     }
 
-    private final static int[] toIntArray(List<Integer> list){
+    private final static int[] toIntArray(List<Integer> list) {
         int[] ret = new int[list.size()];
-        for(int i = 0;i < ret.length;i++) {
+        for (int i = 0; i < ret.length; i++) {
             ret[i] = list.get(i);
         }
         return ret;
@@ -113,7 +116,7 @@
         PackageManager pm = context.getPackageManager();
         final List<UserInfo> users = um.getUsers(true);
         if (DEBUG) {
-            Slog.w(TAG, "Iterating over "+users.size() + " profiles.");
+            Slog.w(TAG, "Iterating over " + users.size() + " profiles.");
         }
 
         List<Integer> uids = new ArrayList();
@@ -122,23 +125,23 @@
 
         // Add in all the apps for every user/profile.
         for (UserInfo profile : users) {
-          List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
-          for (int j = 0; j < pi.size(); j++) {
-              if (pi.get(j).applicationInfo != null) {
-                  uids.add(pi.get(j).applicationInfo.uid);
-                  versions.add(pi.get(j).versionCode);
-                  apps.add(pi.get(j).packageName);
-              }
-          }
+            List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
+            for (int j = 0; j < pi.size(); j++) {
+                if (pi.get(j).applicationInfo != null) {
+                    uids.add(pi.get(j).applicationInfo.uid);
+                    versions.add(pi.get(j).versionCode);
+                    apps.add(pi.get(j).packageName);
+                }
+            }
         }
         sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
-            String[apps.size()]));
+                String[apps.size()]));
         if (DEBUG) {
-            Slog.w(TAG, "Sent data for "+uids.size() +" apps");
+            Slog.w(TAG, "Sent data for " + uids.size() + " apps");
         }
     }
 
-    public final static class AppUpdateReceiver extends BroadcastReceiver  {
+    public final static class AppUpdateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
@@ -147,7 +150,7 @@
              * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
              */
             if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
-                intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                 return; // Keep only replacing or normal add and remove.
             }
             synchronized (sStatsdLock) {
@@ -180,9 +183,9 @@
                 }
             }
         }
-    };
+    }
 
-    public final static class AnomalyAlarmReceiver extends BroadcastReceiver  {
+    public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
@@ -200,7 +203,7 @@
             }
             // AlarmManager releases its own wakelock here.
         }
-    };
+    }
 
     public final static class PollingAlarmReceiver extends BroadcastReceiver {
         @Override
@@ -220,7 +223,7 @@
             }
             // AlarmManager releases its own wakelock here.
         }
-    };
+    }
 
     @Override // Binder call
     public void setAnomalyAlarm(long timestampMs) {
@@ -286,33 +289,34 @@
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
 
     @Override // Binder call
-    public String pullData(int pullCode) {
+    public StatsLogEventWrapper[] pullData(int pullCode) {
         enforceCallingPermission();
-        if (DEBUG) Slog.d(TAG, "Fetching " + pullCode);
+        if (DEBUG) {
+            Slog.d(TAG, "Pulling " + pullCode);
+        }
 
-        StringBuilder s = new StringBuilder(); // TODO: use and return a Parcel instead of a string
+        List<StatsLogEventWrapper> ret = new ArrayList<>();
         switch (pullCode) {
-            case PULL_CODE_KERNEL_WAKELOCKS:
+            case PULL_CODE_KERNEL_WAKELOCKS: {
                 final KernelWakelockStats wakelockStats =
                         mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
-
                 for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
                     String name = ent.getKey();
                     KernelWakelockStats.Entry kws = ent.getValue();
-                    s.append("Wakelock ")
-                            .append(name)
-                            .append(", time=")
-                            .append(kws.mTotalTime)
-                            .append(", count=")
-                            .append(kws.mCount)
-                            .append('\n');
+                    StatsLogEventWrapper e = new StatsLogEventWrapper(101, 4);
+                    e.writeInt(kws.mCount);
+                    e.writeInt(kws.mVersion);
+                    e.writeLong(kws.mTotalTime);
+                    e.writeString(name);
+                    ret.add(e);
                 }
                 break;
+            }
             default:
                 Slog.w(TAG, "No such pollable data as " + pullCode);
                 return null;
         }
-        return s.toString();
+        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
     }
 
     @Override // Binder call
@@ -331,7 +335,9 @@
 
     // Lifecycle and related code
 
-    /** Fetches the statsd IBinder service */
+    /**
+     * Fetches the statsd IBinder service
+     */
     private static IStatsManager fetchStatsdService() {
         return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
     }
@@ -363,13 +369,17 @@
         }
     }
 
-    /** Now that the android system is ready, StatsCompanion is ready too, so inform statsd. */
+    /**
+     * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
+     */
     private void systemReady() {
         if (DEBUG) Slog.d(TAG, "Learned that systemReady");
         sayHiToStatsd();
     }
 
-    /** Tells statsd that statscompanion is ready. If the binder call returns, link to statsd. */
+    /**
+     * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
+     */
     private void sayHiToStatsd() {
         synchronized (sStatsdLock) {
             if (sStatsd != null) {
@@ -398,14 +408,14 @@
                 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
                 filter.addDataScheme("package");
                 mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
-                    null);
+                        null);
 
                 // Setup receiver for user initialize (which happens once for a new user) and
                 // if a user is removed.
                 filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
                 filter.addAction(Intent.ACTION_USER_REMOVED);
                 mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
-                    filter, null, null);
+                        filter, null, null);
 
                 // Pull the latest state of UID->app name, version mapping when statsd starts.
                 informAllUidsLocked(mContext);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c59f44e..80f6a4b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -175,6 +175,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
@@ -1682,6 +1683,10 @@
             return getCallingUid() == Process.myUid();
         }
 
+        void binderWithCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+             Binder.withCleanCallingIdentity(action);
+        }
+
         final int userHandleGetCallingUserId() {
             return UserHandle.getUserId(binderGetCallingUid());
         }
@@ -9023,6 +9028,31 @@
     }
 
     @Override
+    public boolean setTime(ComponentName who, long millis) {
+        Preconditions.checkNotNull(who, "ComponentName is null in setTime");
+        getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        // Don't allow set time when auto time is on.
+        if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
+            return false;
+        }
+        mInjector.binderWithCleanCallingIdentity(() -> mInjector.getAlarmManager().setTime(millis));
+        return true;
+    }
+
+    @Override
+    public boolean setTimeZone(ComponentName who, String timeZone) {
+        Preconditions.checkNotNull(who, "ComponentName is null in setTimeZone");
+        getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        // Don't allow set timezone when auto timezone is on.
+        if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
+            return false;
+        }
+        mInjector.binderWithCleanCallingIdentity(() ->
+            mInjector.getAlarmManager().setTimeZone(timeZone));
+        return true;
+    }
+
+    @Override
     public void setSecureSetting(ComponentName who, String setting, String value) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         int callingUserId = mInjector.userHandleGetCallingUserId();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b78fcdd..d9db22e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1551,11 +1551,6 @@
             traceEnd();
         }
 
-        // Statsd helper
-        traceBeginAndSlog("StartStatsCompanionService");
-        mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
-        traceEnd();
-
         // Before things start rolling, be sure we have decided whether
         // we are in safe mode.
         final boolean safeMode = wm.detectSafeMode();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 5471715..ae4b569 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.devicepolicy;
 
+import android.app.AlarmManager;
 import android.app.IActivityManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -34,11 +35,13 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.security.KeyChain;
+import android.support.annotation.NonNull;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Pair;
 import android.view.IWindowManager;
 
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.widget.LockPatternUtils;
 
 import java.io.File;
@@ -194,6 +197,9 @@
         }
 
         @Override
+        AlarmManager getAlarmManager() {return services.alarmManager;}
+
+        @Override
         LockPatternUtils newLockPatternUtils() {
             return services.lockPatternUtils;
         }
@@ -234,6 +240,11 @@
         }
 
         @Override
+        void binderWithCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+            context.binder.withCleanCallingIdentity(action);
+        }
+
+        @Override
         int binderGetCallingUid() {
             return context.binder.getCallingUid();
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a8bf8f1..e1e9cf5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3090,6 +3090,47 @@
         assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
     }
 
+    public void testSetTime() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        dpm.setTime(admin1, 0);
+        verify(getServices().alarmManager).setTime(0);
+    }
+
+    public void testSetTimeFailWithPO() throws Exception {
+        setupProfileOwner();
+        assertExpectException(SecurityException.class, null, () -> dpm.setTime(admin1, 0));
+    }
+
+    public void testSetTimeWithAutoTimeOn() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME, 0))
+                .thenReturn(1);
+        assertFalse(dpm.setTime(admin1, 0));
+    }
+
+    public void testSetTimeZone() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        dpm.setTimeZone(admin1, "Asia/Shanghai");
+        verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
+    }
+
+    public void testSetTimeZoneFailWithPO() throws Exception {
+        setupProfileOwner();
+        assertExpectException(SecurityException.class, null,
+                () -> dpm.setTimeZone(admin1, "Asia/Shanghai"));
+    }
+
+    public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME_ZONE, 0))
+                .thenReturn(1);
+        assertFalse(dpm.setTimeZone(admin1, "Asia/Shanghai"));
+    }
+
     public void testGetLastBugReportRequestTime() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 9702118..7e11e87 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -30,8 +30,12 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
+import android.support.annotation.NonNull;
 import android.test.mock.MockContext;
 import android.util.ArrayMap;
+import android.util.ExceptionUtils;
+
+import com.android.internal.util.FunctionalUtils;
 
 import org.junit.Assert;
 
@@ -95,6 +99,21 @@
             callingPid = (int) token;
         }
 
+        public void withCleanCallingIdentity(@NonNull FunctionalUtils.ThrowingRunnable action) {
+            long callingIdentity = clearCallingIdentity();
+            Throwable throwableToPropagate = null;
+            try {
+                action.run();
+            } catch (Throwable throwable) {
+                throwableToPropagate = throwable;
+            } finally {
+                restoreCallingIdentity(callingIdentity);
+                if (throwableToPropagate != null) {
+                    throw ExceptionUtils.propagate(throwableToPropagate);
+                }
+            }
+        }
+
         public int getCallingUid() {
             return callingUid;
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 56a3fb0..b8e8946 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -89,6 +89,9 @@
     public void setUp() throws Exception {
         if (!sOneTimeSetupDone) {
             sOneTimeSetupDone = true;
+
+            // Allows to mock package local classes and methods
+            System.setProperty("dexmaker.share_classloader", "true");
             MockitoAnnotations.initMocks(this);
         }
 
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index c3b2c48..9ccdd56 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -118,14 +118,8 @@
         }
 
         final int uid = Binder.getCallingUid();
-        callback.asBinder().linkToDeath(new DeathRecipient() {
-            @Override
-            public void binderDied() {
-                onAppCallbackDied(uid, subscriptionId);
-            }
-        }, 0);
 
-        return initialize(subscriptionId, new MbmsDownloadSessionCallback() {
+        int result = initialize(subscriptionId, new MbmsDownloadSessionCallback() {
             @Override
             public void onError(int errorCode, String message) {
                 try {
@@ -153,6 +147,17 @@
                 }
             }
         });
+
+        if (result == MbmsErrors.SUCCESS) {
+            callback.asBinder().linkToDeath(new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }, 0);
+        }
+
+        return result;
     }
 
     /**
@@ -251,17 +256,6 @@
             throw new NullPointerException("Callback must not be null");
         }
 
-        DeathRecipient deathRecipient = new DeathRecipient() {
-            @Override
-            public void binderDied() {
-                onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
-                mDownloadCallbackBinderMap.remove(callback.asBinder());
-                mDownloadCallbackDeathRecipients.remove(callback.asBinder());
-            }
-        };
-        mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
-        callback.asBinder().linkToDeath(deathRecipient, 0);
-
         DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
             @Override
             protected void onRemoteException(RemoteException e) {
@@ -269,9 +263,23 @@
             }
         };
 
-        mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+        int result = registerStateCallback(downloadRequest, exposedCallback);
 
-        return registerStateCallback(downloadRequest, exposedCallback);
+        if (result == MbmsErrors.SUCCESS) {
+            DeathRecipient deathRecipient = new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                    mDownloadCallbackBinderMap.remove(callback.asBinder());
+                    mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+                }
+            };
+            mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
+            callback.asBinder().linkToDeath(deathRecipient, 0);
+            mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+        }
+
+        return result;
     }
 
     /**
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 65b726d..a238153 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -70,14 +70,8 @@
         }
 
         final int uid = Binder.getCallingUid();
-        callback.asBinder().linkToDeath(new DeathRecipient() {
-            @Override
-            public void binderDied() {
-                onAppCallbackDied(uid, subscriptionId);
-            }
-        }, 0);
 
-        return initialize(new MbmsStreamingSessionCallback() {
+        int result = initialize(new MbmsStreamingSessionCallback() {
             @Override
             public void onError(final int errorCode, final String message) {
                 try {
@@ -105,6 +99,17 @@
                 }
             }
         }, subscriptionId);
+
+        if (result == MbmsErrors.SUCCESS) {
+            callback.asBinder().linkToDeath(new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }, 0);
+        }
+
+        return result;
     }
 
 
@@ -161,14 +166,8 @@
         }
 
         final int uid = Binder.getCallingUid();
-        callback.asBinder().linkToDeath(new DeathRecipient() {
-            @Override
-            public void binderDied() {
-                onAppCallbackDied(uid, subscriptionId);
-            }
-        }, 0);
 
-        return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
+        int result = startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
             @Override
             public void onError(final int errorCode, final String message) {
                 try {
@@ -215,6 +214,17 @@
                 }
             }
         });
+
+        if (result == MbmsErrors.SUCCESS) {
+            callback.asBinder().linkToDeath(new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }, 0);
+        }
+
+        return result;
     }
 
     /**
diff --git a/tools/locked_region_code_injection/Android.mk b/tools/locked_region_code_injection/Android.mk
index bb5f4d6..3f65151 100644
--- a/tools/locked_region_code_injection/Android.mk
+++ b/tools/locked_region_code_injection/Android.mk
@@ -6,10 +6,10 @@
 LOCAL_MODULE := lockedregioncodeinjection
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    asm-6.0_BETA \
-    asm-commons-6.0_BETA \
-    asm-tree-6.0_BETA \
-    asm-analysis-6.0_BETA \
+    asm-6.0 \
+    asm-commons-6.0 \
+    asm-tree-6.0 \
+    asm-analysis-6.0 \
     guava-21.0 \
 
 include $(BUILD_HOST_JAVA_LIBRARY)