Merge "Only log a warning if it's an actual violation"
diff --git a/Android.bp b/Android.bp
index 13f6b6e..c89cc40 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,6 +75,7 @@
     "core/*",
     "libs/*",
     "media/*",
+    "proto",
     "tools/*",
     "native/android",
     "native/graphics/jni",
diff --git a/api/test-current.txt b/api/test-current.txt
index dd0e999..7930c47 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32047,12 +32047,10 @@
   public static final class StrictMode.ViolationInfo implements android.os.Parcelable {
     ctor public StrictMode.ViolationInfo();
     ctor public StrictMode.ViolationInfo(java.lang.Throwable, int);
-    ctor public deprecated StrictMode.ViolationInfo(java.lang.String, java.lang.Throwable, int);
     ctor public StrictMode.ViolationInfo(android.os.Parcel);
     ctor public StrictMode.ViolationInfo(android.os.Parcel, boolean);
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
-    method public java.lang.String getMessagePrefix();
     method public java.lang.String getStackTrace();
     method public java.lang.String getViolationDetails();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 280b1e80..351ec4e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -501,7 +501,7 @@
     public static final int PROCESS_STATE_SERVICE = 11;
 
     /** @hide Process is in the background running a receiver.   Note that from the
-     * perspective of oom_adj receivers run at a higher foreground level, but for our
+     * perspective of oom_adj, receivers run at a higher foreground level, but for our
      * prioritization here that is not necessary and putting them below services means
      * many fewer changes in some process states as they receive broadcasts. */
     public static final int PROCESS_STATE_RECEIVER = 12;
@@ -525,6 +525,20 @@
     /** @hide Process does not exist. */
     public static final int PROCESS_STATE_NONEXISTENT = 18;
 
+    // NOTE: If PROCESS_STATEs are added or changed, then new fields must be added
+    // to frameworks/base/core/proto/android/app/activitymanager.proto and the following method must
+    // be updated to correctly map between them.
+    /**
+     * Maps ActivityManager.PROCESS_STATE_ values to ActivityManagerProto.ProcessState enum.
+     *
+     * @param amInt a process state of the form ActivityManager.PROCESS_STATE_
+     * @return the value of the corresponding android.app.ActivityManagerProto's ProcessState enum.
+     * @hide
+     */
+    public static final int processStateAmToProto(int amInt) {
+        return amInt * 100;
+    }
+
     /** @hide The lowest process state number */
     public static final int MIN_PROCESS_STATE = PROCESS_STATE_PERSISTENT;
 
@@ -1886,7 +1900,7 @@
     public List<RunningTaskInfo> getRunningTasks(int maxNum)
             throws SecurityException {
         try {
-            return getService().getTasks(maxNum, 0);
+            return getService().getTasks(maxNum);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 117854a..76a5612 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -115,7 +115,9 @@
             in PersistableBundle persistentState, in CharSequence description);
     String getCallingPackage(in IBinder token);
     ComponentName getCallingActivity(in IBinder token);
-    List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, int flags);
+    List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
+    List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType,
+            int ignoreWindowingMode);
     void moveTaskToFront(int task, int flags, in Bundle options);
     void moveTaskBackwards(int task);
     int getTaskForActivity(in IBinder token, in boolean onlyRoot);
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 251863c..de27b4f 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -102,7 +102,7 @@
     public static final int ACTIVITY_TYPE_STANDARD = 1;
     /** Home/Launcher activity type. */
     public static final int ACTIVITY_TYPE_HOME = 2;
-    /** Recents/Overview activity type. */
+    /** Recents/Overview activity type. There is only one activity with this type in the system. */
     public static final int ACTIVITY_TYPE_RECENTS = 3;
     /** Assistant activity type. */
     public static final int ACTIVITY_TYPE_ASSISTANT = 4;
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java
index 28cf42f..eb61c153 100644
--- a/core/java/android/net/metrics/DefaultNetworkEvent.java
+++ b/core/java/android/net/metrics/DefaultNetworkEvent.java
@@ -16,73 +16,43 @@
 
 package android.net.metrics;
 
+import static android.net.ConnectivityManager.NETID_UNSET;
+
 import android.net.NetworkCapabilities;
-import android.os.Parcel;
-import android.os.Parcelable;
 
 /**
  * An event recorded by ConnectivityService when there is a change in the default network.
  * {@hide}
  */
-public final class DefaultNetworkEvent implements Parcelable {
+public class DefaultNetworkEvent {
+
     // The ID of the network that has become the new default or NETID_UNSET if none.
-    public final int netId;
+    public int netId = NETID_UNSET;
     // The list of transport types of the new default network, for example TRANSPORT_WIFI, as
     // defined in NetworkCapabilities.java.
-    public final int[] transportTypes;
+    public int[] transportTypes = new int[0];
     // The ID of the network that was the default before or NETID_UNSET if none.
-    public final int prevNetId;
+    public int prevNetId = NETID_UNSET;
     // Whether the previous network had IPv4/IPv6 connectivity.
-    public final boolean prevIPv4;
-    public final boolean prevIPv6;
-
-    public DefaultNetworkEvent(int netId, int[] transportTypes,
-                int prevNetId, boolean prevIPv4, boolean prevIPv6) {
-        this.netId = netId;
-        this.transportTypes = transportTypes;
-        this.prevNetId = prevNetId;
-        this.prevIPv4 = prevIPv4;
-        this.prevIPv6 = prevIPv6;
-    }
-
-    private DefaultNetworkEvent(Parcel in) {
-        this.netId = in.readInt();
-        this.transportTypes = in.createIntArray();
-        this.prevNetId = in.readInt();
-        this.prevIPv4 = (in.readByte() > 0);
-        this.prevIPv6 = (in.readByte() > 0);
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(netId);
-        out.writeIntArray(transportTypes);
-        out.writeInt(prevNetId);
-        out.writeByte(prevIPv4 ? (byte) 1 : (byte) 0);
-        out.writeByte(prevIPv6 ? (byte) 1 : (byte) 0);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
+    public boolean prevIPv4;
+    public boolean prevIPv6;
 
     @Override
     public String toString() {
-      String prevNetwork = String.valueOf(prevNetId);
-      String newNetwork = String.valueOf(netId);
-      if (prevNetId != 0) {
-          prevNetwork += ":" + ipSupport();
-      }
-      if (netId != 0) {
-          newNetwork += ":" + NetworkCapabilities.transportNamesOf(transportTypes);
-      }
-      return String.format("DefaultNetworkEvent(%s -> %s)", prevNetwork, newNetwork);
+        String prevNetwork = String.valueOf(prevNetId);
+        String newNetwork = String.valueOf(netId);
+        if (prevNetId != 0) {
+            prevNetwork += ":" + ipSupport();
+        }
+        if (netId != 0) {
+            newNetwork += ":" + NetworkCapabilities.transportNamesOf(transportTypes);
+        }
+        return String.format("DefaultNetworkEvent(%s -> %s)", prevNetwork, newNetwork);
     }
 
     private String ipSupport() {
         if (prevIPv4 && prevIPv6) {
-            return "DUAL";
+            return "IPv4v6";
         }
         if (prevIPv6) {
             return "IPv6";
@@ -92,15 +62,4 @@
         }
         return "NONE";
     }
-
-    public static final Parcelable.Creator<DefaultNetworkEvent> CREATOR
-        = new Parcelable.Creator<DefaultNetworkEvent>() {
-        public DefaultNetworkEvent createFromParcel(Parcel in) {
-            return new DefaultNetworkEvent(in);
-        }
-
-        public DefaultNetworkEvent[] newArray(int size) {
-            return new DefaultNetworkEvent[size];
-        }
-    };
 }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f52d94e..ee3e5bc 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -34,6 +34,7 @@
 import android.util.Slog;
 import android.view.IWindowManager;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.HexDump;
@@ -1647,7 +1648,7 @@
 
     private static class AndroidCloseGuardReporter implements CloseGuard.Reporter {
         public void report(String message, Throwable allocationSite) {
-            onVmPolicyViolation(message, allocationSite);
+            onVmPolicyViolation(allocationSite);
         }
     }
 
@@ -1686,7 +1687,7 @@
             long instances = instanceCounts[i];
             if (instances > limit) {
                 Throwable tr = new InstanceCountViolation(klass, instances, limit);
-                onVmPolicyViolation(tr.getMessage(), tr);
+                onVmPolicyViolation(tr);
             }
         }
     }
@@ -1810,22 +1811,24 @@
 
     /** @hide */
     public static void onSqliteObjectLeaked(String message, Throwable originStack) {
-        onVmPolicyViolation(message, originStack);
+        Throwable t = new Throwable(message);
+        t.setStackTrace(originStack.getStackTrace());
+        onVmPolicyViolation(t);
     }
 
     /** @hide */
     public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
-        onVmPolicyViolation(null, originStack);
+        onVmPolicyViolation(originStack);
     }
 
     /** @hide */
     public static void onIntentReceiverLeaked(Throwable originStack) {
-        onVmPolicyViolation(null, originStack);
+        onVmPolicyViolation(originStack);
     }
 
     /** @hide */
     public static void onServiceConnectionLeaked(Throwable originStack) {
-        onVmPolicyViolation(null, originStack);
+        onVmPolicyViolation(originStack);
     }
 
     /** @hide */
@@ -1834,7 +1837,7 @@
         if ((sVmPolicy.mask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) {
             throw new FileUriExposedException(message);
         } else {
-            onVmPolicyViolation(null, new Throwable(message));
+            onVmPolicyViolation(new Throwable(message));
         }
     }
 
@@ -1846,7 +1849,7 @@
                         + location
                         + " without permission grant flags; did you forget"
                         + " FLAG_GRANT_READ_URI_PERMISSION?";
-        onVmPolicyViolation(null, new Throwable(message));
+        onVmPolicyViolation(new Throwable(message));
     }
 
     /** @hide */
@@ -1876,10 +1879,9 @@
             } catch (UnknownHostException ignored) {
             }
         }
-
+        msg += HexDump.dumpHexString(firstPacket).trim() + " ";
         final boolean forceDeath = (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0;
-        onVmPolicyViolation(
-                HexDump.dumpHexString(firstPacket).trim(), new Throwable(msg), forceDeath);
+        onVmPolicyViolation(new Throwable(msg), forceDeath);
     }
 
     /** @hide */
@@ -1889,24 +1891,23 @@
 
     /** @hide */
     public static void onUntaggedSocket() {
-        onVmPolicyViolation(null, new Throwable(UNTAGGED_SOCKET_VIOLATION_MESSAGE));
+        onVmPolicyViolation(new Throwable(UNTAGGED_SOCKET_VIOLATION_MESSAGE));
     }
 
     // Map from VM violation fingerprint to uptime millis.
     private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
 
     /** @hide */
-    public static void onVmPolicyViolation(String message, Throwable originStack) {
-        onVmPolicyViolation(message, originStack, false);
+    public static void onVmPolicyViolation(Throwable originStack) {
+        onVmPolicyViolation(originStack, false);
     }
 
     /** @hide */
-    public static void onVmPolicyViolation(
-            String message, Throwable originStack, boolean forceDeath) {
+    public static void onVmPolicyViolation(Throwable originStack, boolean forceDeath) {
         final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0;
         final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath;
         final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0;
-        final ViolationInfo info = new ViolationInfo(message, originStack, sVmPolicy.mask);
+        final ViolationInfo info = new ViolationInfo(originStack, sVmPolicy.mask);
 
         // Erase stuff not relevant for process-wide violations
         info.numAnimationsRunning = 0;
@@ -2224,7 +2225,7 @@
             // StrictMode not enabled.
             return;
         }
-        ((AndroidBlockGuardPolicy) policy).onUnbufferedIO();
+        policy.onUnbufferedIO();
     }
 
     /** @hide */
@@ -2234,7 +2235,7 @@
             // StrictMode not enabled.
             return;
         }
-        ((AndroidBlockGuardPolicy) policy).onReadFromDisk();
+        policy.onReadFromDisk();
     }
 
     /** @hide */
@@ -2244,12 +2245,11 @@
             // StrictMode not enabled.
             return;
         }
-        ((AndroidBlockGuardPolicy) policy).onWriteToDisk();
+        policy.onWriteToDisk();
     }
 
-    // Guarded by StrictMode.class
-    private static final HashMap<Class, Integer> sExpectedActivityInstanceCount =
-            new HashMap<Class, Integer>();
+    @GuardedBy("StrictMode.class")
+    private static final HashMap<Class, Integer> sExpectedActivityInstanceCount = new HashMap<>();
 
     /**
      * Returns an object that is used to track instances of activites. The activity should store a
@@ -2324,7 +2324,7 @@
         long instances = VMDebug.countInstancesOfClass(klass, false);
         if (instances > limit) {
             Throwable tr = new InstanceCountViolation(klass, instances, limit);
-            onVmPolicyViolation(tr.getMessage(), tr);
+            onVmPolicyViolation(tr);
         }
     }
 
@@ -2336,9 +2336,6 @@
      */
     @TestApi
     public static final class ViolationInfo implements Parcelable {
-        /** Some VM violations provide additional information outside the throwable. */
-        @Nullable private final String mMessagePrefix;
-
         /** Stack and violation details. */
         @Nullable private final Throwable mThrowable;
 
@@ -2382,24 +2379,12 @@
 
         /** Create an uninitialized instance of ViolationInfo */
         public ViolationInfo() {
-            mMessagePrefix = null;
             mThrowable = null;
             policy = 0;
         }
 
-        /** Create an instance of ViolationInfo. */
+        /** Create an instance of ViolationInfo initialized from an exception. */
         public ViolationInfo(Throwable tr, int policy) {
-            this(null, tr, policy);
-        }
-
-        /**
-         * Create an instance of ViolationInfo initialized from an exception with a message prefix.
-         *
-         * @deprecated prefixes belong in the Throwable.
-         */
-        @Deprecated
-        public ViolationInfo(String messagePrefix, Throwable tr, int policy) {
-            this.mMessagePrefix = messagePrefix;
             this.mThrowable = tr;
             violationUptimeMillis = SystemClock.uptimeMillis();
             this.policy = policy;
@@ -2462,17 +2447,7 @@
         }
 
         /**
-         * A handful of VM violations provide extra information that should be presented before
-         * {@link #getViolationDetails()}.
-         *
-         * @hide
-         */
-        @TestApi
-        public String getMessagePrefix() {
-            return mMessagePrefix != null ? mMessagePrefix : "";
-        }
-
-        /** If this violation has a useful stack trace.
+         * If this violation has a useful stack trace.
          *
          * @hide
          */
@@ -2552,7 +2527,6 @@
          *     should be removed.
          */
         public ViolationInfo(Parcel in, boolean unsetGatheringBit) {
-            mMessagePrefix = in.readString();
             mThrowable = (Throwable) in.readSerializable();
             int binderStackSize = in.readInt();
             for (int i = 0; i < binderStackSize; i++) {
@@ -2576,7 +2550,6 @@
         /** Save a ViolationInfo instance to a parcel. */
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(mMessagePrefix);
             dest.writeSerializable(mThrowable);
             dest.writeInt(mBinderStack.size());
             for (Throwable t : mBinderStack) {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index fde2416..9a1dcbb 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -535,14 +535,15 @@
          * 16 digits, or 15 digits starting with 108:
          *
          * <pre class="prettyprint">
-         * import android.service.autofill.Validators;
+         * import static android.service.autofill.Validators.and;
+         * import static android.service.autofill.Validators.or;
          *
          * Validator validator =
          *   and(
          *     new LuhnChecksumValidator(ccNumberId),
          *     or(
-         *       new RegexValidator(ccNumberId, Pattern.compile(""^\\d{16}$")),
-         *       new RegexValidator(ccNumberId, Pattern.compile(""^108\\d{12}$"))
+         *       new RegexValidator(ccNumberId, Pattern.compile("^\\d{16}$")),
+         *       new RegexValidator(ccNumberId, Pattern.compile("^108\\d{12}$"))
          *     )
          *   );
          * </pre>
@@ -562,14 +563,14 @@
          * 4 digits on each field:
          *
          * <pre class="prettyprint">
-         * import android.service.autofill.Validators;
+         * import static android.service.autofill.Validators.and;
          *
          * Validator validator =
          *   and(
-         *     new RegexValidator(ccNumberId1, Pattern.compile(""^\\d{4}$")),
-         *     new RegexValidator(ccNumberId2, Pattern.compile(""^\\d{4}$")),
-         *     new RegexValidator(ccNumberId3, Pattern.compile(""^\\d{4}$")),
-         *     new RegexValidator(ccNumberId4, Pattern.compile(""^\\d{4}$"))
+         *     new RegexValidator(ccNumberId1, Pattern.compile("^\\d{4}$")),
+         *     new RegexValidator(ccNumberId2, Pattern.compile("^\\d{4}$")),
+         *     new RegexValidator(ccNumberId3, Pattern.compile("^\\d{4}$")),
+         *     new RegexValidator(ccNumberId4, Pattern.compile("^\\d{4}$"))
          *   );
          * </pre>
          *
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 39874e8..3861695 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -37,7 +37,7 @@
     }
 
     /**
-     * Returns the file extension or an empty string iff there is no
+     * Returns the file extension or an empty string if there is no
      * extension. This method is a convenience method for obtaining the
      * extension of a url and has undefined results for other Strings.
      * @param url
@@ -76,7 +76,7 @@
     /**
      * Return {@code true} if the given MIME type has an entry in the map.
      * @param mimeType A MIME type (i.e. text/plain)
-     * @return {@code true} iff there is a mimeType entry in the map.
+     * @return {@code true} if there is a mimeType entry in the map.
      */
     public boolean hasMimeType(String mimeType) {
         return MimeUtils.hasMimeType(mimeType);
@@ -85,7 +85,7 @@
     /**
      * Return the MIME type for the given extension.
      * @param extension A file extension without the leading '.'
-     * @return The MIME type for the given extension or {@code null} iff there is none.
+     * @return The MIME type for the given extension or {@code null} if there is none.
      */
     @Nullable
     public String getMimeTypeFromExtension(String extension) {
@@ -100,7 +100,7 @@
     /**
      * Return {@code true} if the given extension has a registered MIME type.
      * @param extension A file extension without the leading '.'
-     * @return {@code true} iff there is an extension entry in the map.
+     * @return {@code true} if there is an extension entry in the map.
      */
     public boolean hasExtension(String extension) {
         return MimeUtils.hasExtension(extension);
@@ -111,7 +111,7 @@
      * MIME types map to multiple extensions. This call will return the most
      * common extension for the given MIME type.
      * @param mimeType A MIME type (i.e. text/plain)
-     * @return The extension for the given MIME type or {@code null} iff there is none.
+     * @return The extension for the given MIME type or {@code null} if there is none.
      */
     @Nullable
     public String getExtensionFromMimeType(String mimeType) {
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index c2f121a..84c000a 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -137,7 +137,7 @@
     }
 
     /**
-     * @return {@code true} iff the url is correctly URL encoded
+     * @return {@code true} if the url is correctly URL encoded
      */
     static boolean verifyURLEncoding(String url) {
         int count = url.length();
@@ -171,14 +171,14 @@
     }
 
     /**
-     * @return {@code true} iff the url is an asset file.
+     * @return {@code true} if the url is an asset file.
      */
     public static boolean isAssetUrl(String url) {
         return (null != url) && url.startsWith(ASSET_BASE);
     }
 
     /**
-     * @return {@code true} iff the url is a resource file.
+     * @return {@code true} if the url is a resource file.
      * @hide
      */
     public static boolean isResourceUrl(String url) {
@@ -186,7 +186,7 @@
     }
 
     /**
-     * @return {@code true} iff the url is a proxy url to allow cookieless network
+     * @return {@code true} if the url is a proxy url to allow cookieless network
      * requests from a file url.
      * @deprecated Cookieless proxy is no longer supported.
      */
@@ -196,7 +196,7 @@
     }
 
     /**
-     * @return {@code true} iff the url is a local file.
+     * @return {@code true} if the url is a local file.
      */
     public static boolean isFileUrl(String url) {
         return (null != url) && (url.startsWith(FILE_BASE) &&
@@ -205,28 +205,28 @@
     }
 
     /**
-     * @return {@code true} iff the url is an about: url.
+     * @return {@code true} if the url is an about: url.
      */
     public static boolean isAboutUrl(String url) {
         return (null != url) && url.startsWith("about:");
     }
 
     /**
-     * @return {@code true} iff the url is a data: url.
+     * @return {@code true} if the url is a data: url.
      */
     public static boolean isDataUrl(String url) {
         return (null != url) && url.startsWith("data:");
     }
 
     /**
-     * @return {@code true} iff the url is a javascript: url.
+     * @return {@code true} if the url is a javascript: url.
      */
     public static boolean isJavaScriptUrl(String url) {
         return (null != url) && url.startsWith("javascript:");
     }
 
     /**
-     * @return {@code true} iff the url is an http: url.
+     * @return {@code true} if the url is an http: url.
      */
     public static boolean isHttpUrl(String url) {
         return (null != url) &&
@@ -235,7 +235,7 @@
     }
 
     /**
-     * @return {@code true} iff the url is an https: url.
+     * @return {@code true} if the url is an https: url.
      */
     public static boolean isHttpsUrl(String url) {
         return (null != url) &&
@@ -244,7 +244,7 @@
     }
 
     /**
-     * @return {@code true} iff the url is a network url.
+     * @return {@code true} if the url is a network url.
      */
     public static boolean isNetworkUrl(String url) {
         if (url == null || url.length() == 0) {
@@ -254,14 +254,14 @@
     }
 
     /**
-     * @return {@code true} iff the url is a content: url.
+     * @return {@code true} if the url is a content: url.
      */
     public static boolean isContentUrl(String url) {
         return (null != url) && url.startsWith(CONTENT_BASE);
     }
 
     /**
-     * @return {@code true} iff the url is valid.
+     * @return {@code true} if the url is valid.
      */
     public static boolean isValidUrl(String url) {
         if (url == null || url.length() == 0) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index dfc81b2..259bf60 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1103,7 +1103,7 @@
     /**
      * Gets whether this WebView has a back history item.
      *
-     * @return {@code true} iff this WebView has a back history item
+     * @return {@code true} if this WebView has a back history item
      */
     public boolean canGoBack() {
         checkThread();
@@ -1121,7 +1121,7 @@
     /**
      * Gets whether this WebView has a forward history item.
      *
-     * @return {@code true} iff this WebView has a forward history item
+     * @return {@code true} if this WebView has a forward history item
      */
     public boolean canGoForward() {
         checkThread();
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 48e427f..797bdfb 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -137,7 +137,7 @@
     }
 
     /**
-     * Load the native library for the given package name iff that package
+     * Load the native library for the given package name if that package
      * name is the same as the one providing the webview.
      */
     public static int loadWebViewNativeLibraryFromPackage(String packageName,
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 59b761f..c4f22ee 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -35,6 +35,7 @@
 #include <hidl/HidlTransportSupport.h>
 #include <hwbinder/ProcessState.h>
 #include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include <vintf/parse_string.h>
 #include <utils/misc.h>
 
@@ -261,14 +262,9 @@
         JNIEnv *env,
         jobject thiz,
         jstring serviceNameObj) {
-    if (serviceNameObj == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException", NULL);
-        return;
-    }
-
-    const char *serviceName = env->GetStringUTFChars(serviceNameObj, NULL);
-    if (serviceName == NULL) {
-        return;  // XXX exception already pending?
+    ScopedUtfChars str(env, serviceNameObj);
+    if (str.c_str() == nullptr) {
+        return;  // NPE will be pending.
     }
 
     sp<hardware::IBinder> binder = JHwBinder::GetNativeBinder(env, thiz);
@@ -284,15 +280,12 @@
         return;
     }
 
-    Return<bool> ret = manager->add(serviceName, base);
-
-    env->ReleaseStringUTFChars(serviceNameObj, serviceName);
-    serviceName = NULL;
+    Return<bool> ret = manager->add(str.c_str(), base);
 
     bool ok = ret.isOk() && ret;
 
     if (ok) {
-        LOG(INFO) << "Starting thread pool.";
+        LOG(INFO) << "HwBinder: Starting thread pool for " << str.c_str();
         ::android::hardware::ProcessState::self()->startThreadPool();
     }
 
@@ -308,28 +301,23 @@
     using ::android::hidl::base::V1_0::IBase;
     using ::android::hardware::details::getRawServiceInternal;
 
-    if (ifaceNameObj == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException", NULL);
-        return NULL;
-    }
-    if (serviceNameObj == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException", NULL);
-        return NULL;
+    std::string ifaceName;
+    {
+        ScopedUtfChars str(env, ifaceNameObj);
+        if (str.c_str() == nullptr) {
+            return nullptr;  // NPE will be pending.
+        }
+        ifaceName = str.c_str();
     }
 
-    const char *ifaceNameCStr = env->GetStringUTFChars(ifaceNameObj, NULL);
-    if (ifaceNameCStr == NULL) {
-        return NULL; // XXX exception already pending?
+    std::string serviceName;
+    {
+        ScopedUtfChars str(env, serviceNameObj);
+        if (str.c_str() == nullptr) {
+            return nullptr;  // NPE will be pending.
+        }
+        serviceName = str.c_str();
     }
-    std::string ifaceName(ifaceNameCStr);
-    env->ReleaseStringUTFChars(ifaceNameObj, ifaceNameCStr);
-
-    const char *serviceNameCStr = env->GetStringUTFChars(serviceNameObj, NULL);
-    if (serviceNameCStr == NULL) {
-        return NULL; // XXX exception already pending?
-    }
-    std::string serviceName(serviceNameCStr);
-    env->ReleaseStringUTFChars(serviceNameObj, serviceNameCStr);
 
     // TODO(b/67981006): true /* retry */
     sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, false /* retry */, false /* getStub */);
@@ -340,7 +328,7 @@
         return NULL;
     }
 
-    LOG(INFO) << "Starting thread pool.";
+    LOG(INFO) << "HwBinder: Starting thread pool for " << serviceName << "::" << ifaceName;
     ::android::hardware::ProcessState::self()->startThreadPool();
 
     return JHwRemoteBinder::NewObject(env, service);
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
new file mode 100644
index 0000000..e87499e
--- /dev/null
+++ b/core/proto/android/app/activitymanager.proto
@@ -0,0 +1,80 @@
+/*
+ * 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";
+
+package android.app;
+
+option java_multiple_files = true;
+
+message ActivityManagerProto {
+
+    // ActivityManager.java PROCESS_STATEs
+    enum ProcessState {
+      // Order matters for process states, so values have been spaced to provide
+      // room for future additions.
+
+      // Not a real process state.
+      PROCESS_STATE_UNKNOWN = -100;
+      // Process is a persistent system process.
+      PROCESS_STATE_PERSISTENT = 0;
+      // Process is a persistent system process and is doing UI.
+      PROCESS_STATE_PERSISTENT_UI = 100;
+      // Process is hosting the current top activities. Note that this covers
+      // all activities that are visible to the user.
+      PROCESS_STATE_TOP = 200;
+      // Process is hosting a foreground service due to a system binding.
+      PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 300;
+      // Process is hosting a foreground service.
+      PROCESS_STATE_FOREGROUND_SERVICE = 400;
+      // Same as PROCESS_STATE_TOP but while device is sleeping.
+      PROCESS_STATE_TOP_SLEEPING = 500;
+      // Process is important to the user, and something they are aware of.
+      PROCESS_STATE_IMPORTANT_FOREGROUND = 600;
+      // Process is important to the user, but not something they are aware of.
+      PROCESS_STATE_IMPORTANT_BACKGROUND = 700;
+      // Process is in the background transient so we will try to keep running.
+      PROCESS_STATE_TRANSIENT_BACKGROUND = 800;
+      // Process is in the background running a backup/restore operation.
+      PROCESS_STATE_BACKUP = 900;
+      // Process is in the background, but it can't restore its state so we want
+      // to try to avoid killing it.
+      PROCESS_STATE_HEAVY_WEIGHT = 1000;
+      // Process is in the background running a service. Unlike oom_adj, this
+      // level is used for both the normal running in background state and the
+      // executing operations state.
+      PROCESS_STATE_SERVICE = 1100;
+      // Process is in the background running a receiver. Note that from the
+      // perspective of oom_adj, receivers run at a higher foreground level, but
+      // for our prioritization here that is not necessary and putting them
+      // below services means many fewer changes in some process states as they
+      // receive broadcasts.
+      PROCESS_STATE_RECEIVER = 1200;
+      // Process is in the background but hosts the home activity.
+      PROCESS_STATE_HOME = 1300;
+      // Process is in the background but hosts the last shown activity.
+      PROCESS_STATE_LAST_ACTIVITY = 1400;
+      // Process is being cached for later use and contains activities.
+      PROCESS_STATE_CACHED_ACTIVITY = 1500;
+      // Process is being cached for later use and is a client of another cached
+      // process that contains activities.
+      PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1600;
+      // Process is being cached for later use and is empty.
+      PROCESS_STATE_CACHED_EMPTY = 1700;
+      // Process does not exist.
+      PROCESS_STATE_NONEXISTENT = 1800;
+    }
+}
diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto
index 4f49744..3e5265a 100644
--- a/core/proto/android/content/intent.proto
+++ b/core/proto/android/content/intent.proto
@@ -15,15 +15,37 @@
  */
 
 syntax = "proto2";
+package android.content;
+
 option java_package = "android.content";
 option java_multiple_files = true;
 
 import "frameworks/base/core/proto/android/os/patternmatcher.proto";
 
-package android.content;
-
 // Next Tag: 13
 message IntentProto {
+    enum DockState {
+        // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+        // the phone is not in any dock.
+        DOCK_STATE_UNDOCKED = 0;
+
+        // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+        // the phone is in a desk dock.
+        DOCK_STATE_DESK = 1;
+
+        // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+        // the phone is in a car dock.
+        DOCK_STATE_CAR = 2;
+
+        // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+        // the phone is in a analog (low end) dock.
+        DOCK_STATE_LE_DESK = 3;
+
+        // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+        // the phone is in a digital (high end) dock.
+        DOCK_STATE_HE_DESK = 4;
+    }
+
     optional string action = 1;
     repeated string categories = 2;
     optional string data = 3;
diff --git a/core/proto/android/os/batterymanager.proto b/core/proto/android/os/batterymanager.proto
new file mode 100644
index 0000000..669bf2d
--- /dev/null
+++ b/core/proto/android/os/batterymanager.proto
@@ -0,0 +1,29 @@
+/*
+ * 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";
+package android.os;
+
+option java_multiple_files = true;
+
+message BatteryManagerProto {
+    enum PlugType {
+        PLUG_TYPE_NONE = 0;
+        PLUG_TYPE_AC = 1;
+        PLUG_TYPE_USB = 2;
+        PLUG_TYPE_WIRELESS = 4;
+    }
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 0887f03..f716ffe 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,6 +20,7 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/libs/incident/proto/android/section.proto";
+import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
 import "frameworks/base/core/proto/android/service/appwidget.proto";
 import "frameworks/base/core/proto/android/service/battery.proto";
 import "frameworks/base/core/proto/android/service/batterystats.proto";
@@ -28,7 +29,6 @@
 import "frameworks/base/core/proto/android/service/netstats.proto";
 import "frameworks/base/core/proto/android/service/notification.proto";
 import "frameworks/base/core/proto/android/service/package.proto";
-import "frameworks/base/core/proto/android/service/power.proto";
 import "frameworks/base/core/proto/android/service/print.proto";
 import "frameworks/base/core/proto/android/service/procstats.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
@@ -106,7 +106,11 @@
         (section).args = "package --proto"
     ];
 
-    optional android.service.power.PowerServiceDumpProto power = 3009;
+    optional com.android.server.power.PowerManagerServiceDumpProto power = 3009 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "power --proto"
+    ];
+
     optional android.service.print.PrintServiceDumpProto print = 3010;
 
     optional android.service.procstats.ProcessStatsServiceDumpProto procstats = 3011 [
diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto
index 3bfe5d6..e9f409d 100644
--- a/core/proto/android/os/powermanager.proto
+++ b/core/proto/android/os/powermanager.proto
@@ -15,10 +15,10 @@
  */
 
 syntax = "proto2";
-option java_multiple_files = true;
-
 package android.os;
 
+option java_multiple_files = true;
+
 message PowerManagerProto {
     /* User activity events in PowerManager.java. */
     enum UserActivityEvent {
@@ -31,4 +31,84 @@
         // Accessibility taking action on behalf of user.
         USER_ACTIVITY_EVENT_ACCESSIBILITY = 3;
     }
+
+    enum WakeLockLevel {
+        // NOTE: Wake lock levels were previously defined as a bit field, except
+        // that only a few combinations were actually supported so the bit field
+        // was removed. This explains why the numbering scheme is so odd. If
+        // adding a new wake lock level, any unused value can be used.
+
+        // Ensures that the CPU is running; the screen and keyboard backlight
+        // will be allowed to go off.
+        PARTIAL_WAKE_LOCK = 1;
+
+        // Ensures that the screen is on (but may be dimmed); the keyboard
+        // backlight will be allowed to go off. If the user presses the power
+        // button, then the SCREEN_DIM_WAKE_LOCK will be implicitly released by
+        // the system, causing both the screen and the CPU to be turned off.
+        SCREEN_DIM_WAKE_LOCK = 6 [deprecated = true];
+
+        // Ensures that the screen is on at full brightness; the keyboard
+        // backlight will be allowed to go off. If the user presses the power
+        // button, then the SCREEN_BRIGHT_WAKE_LOCK will be implicitly released
+        // by the system, causing both the screen and the CPU to be turned off.
+        SCREEN_BRIGHT_WAKE_LOCK = 10 [deprecated = true];
+
+        // Ensures that the screen and keyboard backlight are on at full
+        // brightness. If the user presses the power button, then the
+        // FULL_WAKE_LOCK will be implicitly released by the system, causing
+        // both the screen and the CPU to be turned off.
+        FULL_WAKE_LOCK = 26 [deprecated = true];
+
+        // Turns the screen off when the proximity sensor activates. If the
+        // proximity sensor detects that an object is nearby, the screen turns
+        // off immediately. Shortly after the object moves away, the screen
+        // turns on again.
+        // A proximity wake lock does not prevent the device from falling asleep
+        // unlike FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK and
+        // SCREEN_DIM_WAKE_LOCK. If there is no user activity and no other wake
+        // locks are held, then the device will fall asleep (and lock) as usual.
+        // However, the device will not fall asleep while the screen has been
+        // turned off by the proximity sensor because it effectively counts as
+        // ongoing user activity.
+        PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32;
+
+        // Put the screen in a low power state and allow the CPU to suspend if
+        // no other wake locks are held. This is used by the dream manager to
+        // implement doze mode. It currently has no effect unless the power
+        // manager is in the dozing state.
+        DOZE_WAKE_LOCK = 64;
+
+        // Keep the device awake enough to allow drawing to occur. This is used
+        // by the window manager to allow applications to draw while the system
+        // is dozing. It currently has no effect unless the power manager is in
+        // the dozing state.
+        DRAW_WAKE_LOCK = 128;
+    }
+}
+
+message PowerManagerInternalProto {
+    // Enum values gotten from PowerManagerInternal.java
+    enum Wakefulness {
+        // The device is asleep. It can only be awoken by a call to wakeUp().
+        // The screen should be off or in the process of being turned off by the
+        // display controller. The device typically passes through the dozing
+        // state first.
+        WAKEFULNESS_ASLEEP = 0;
+        // The device is fully awake. It can be put to sleep by a call to
+        // goToSleep(). When the user activity timeout expires, the device may
+        // start dreaming or go to sleep.
+        WAKEFULNESS_AWAKE = 1;
+        // The device is dreaming. It can be awoken by a call to wakeUp(), which
+        // ends the dream. The device goes to sleep when goToSleep() is called,
+        // when the dream ends, or when unplugged. User activity may brighten
+        // the screen but does not end the dream.
+        WAKEFULNESS_DREAMING = 2;
+        // The device is dozing. It is almost asleep but is allowing a special
+        // low-power "doze" dream to run which keeps the display on but lets the
+        // application processor suspend. It can be awoken by a call to wakeUp()
+        // which ends the dream. The device fully goes to sleep if the dream
+        // cannot be started or ends on its own.
+        WAKEFULNESS_DOZING = 3;
+    }
 }
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index f092713..3411c6a 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -599,6 +599,14 @@
     optional bool default_from_system = 6;
 }
 
+message SettingsProto {
+    // Enum values gotten from Settings.java
+    enum ScreenBrightnessMode {
+        SCREEN_BRIGHTNESS_MODE_MANUAL = 0;
+        SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
+    }
+}
+
 message SettingsOperationProto {
     // When the operation happened
     optional int64 timestamp = 1;
diff --git a/core/proto/android/service/power.proto b/core/proto/android/server/powermanagerservice.proto
similarity index 73%
rename from core/proto/android/service/power.proto
rename to core/proto/android/server/powermanagerservice.proto
index 5d53847..d442acf 100644
--- a/core/proto/android/service/power.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -15,16 +15,22 @@
  */
 
 syntax = "proto2";
-package android.service.power;
+package com.android.server.power;
 
 option java_multiple_files = true;
-option java_outer_classname = "PowerServiceProto";
 
+import "frameworks/base/core/proto/android/app/activitymanager.proto";
+import "frameworks/base/core/proto/android/content/intent.proto";
+import "frameworks/base/core/proto/android/os/batterymanager.proto";
 import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/os/powermanager.proto";
 import "frameworks/base/core/proto/android/os/worksource.proto";
-import "frameworks/base/core/proto/android/service/wirelesschargerdetector.proto";
+import "frameworks/base/core/proto/android/providers/settings.proto";
+import "frameworks/base/core/proto/android/server/wirelesschargerdetector.proto";
+import "frameworks/base/core/proto/android/view/display.proto";
 
-message PowerServiceDumpProto {
+message PowerManagerServiceDumpProto {
+    // A com.android.server.power.PowerManagerService.Constants object.
     message ConstantsProto {
         optional bool is_no_cached_wake_locks = 1;
     }
@@ -44,79 +50,14 @@
         optional bool is_screen_dim = 2;
         optional bool is_screen_dream = 3;
     }
-    message UidProto {
-        // Enum values gotten from ActivityManager.java
-        enum ProcessState {
-            // Process is a persistent system process.
-            PROCESS_STATE_PERSISTENT = 0;
-            // Process is a persistent system process and is doing UI.
-            PROCESS_STATE_PERSISTENT_UI = 1;
-            // Process is hosting the current top activities. Note that this
-            // covers all activities that are visible to the user.
-            PROCESS_STATE_TOP = 2;
-            // Process is hosting a foreground service due to a system binding.
-            PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3;
-            // Process is hosting a foreground service.
-            PROCESS_STATE_FOREGROUND_SERVICE = 4;
-            // Same as {@link #PROCESS_STATE_TOP} but while device is sleeping.
-            PROCESS_STATE_TOP_SLEEPING = 5;
-            // Process is important to the user, and something they are aware of.
-            PROCESS_STATE_IMPORTANT_FOREGROUND = 6;
-            // Process is important to the user, but not something they are aware of.
-            PROCESS_STATE_IMPORTANT_BACKGROUND = 7;
-            // Process is in the background running a backup/restore operation.
-            PROCESS_STATE_BACKUP = 8;
-            // Process is in the background, but it can't restore its state so
-            // we want to try to avoid killing it.
-            PROCESS_STATE_HEAVY_WEIGHT = 9;
-            // Process is in the background running a service.
-            PROCESS_STATE_SERVICE = 10;
-            // Process is in the background running a receiver.
-            PROCESS_STATE_RECEIVER = 11;
-            // Process is in the background but hosts the home activity.
-            PROCESS_STATE_HOME = 12;
-            // Process is in the background but hosts the last shown activity.
-            PROCESS_STATE_LAST_ACTIVITY = 13;
-            // Process is being cached for later use and contains activities.
-            PROCESS_STATE_CACHED_ACTIVITY = 14;
-            // Process is being cached for later use and is a client of another
-            // cached process that contains activities.
-            PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15;
-            // Process is being cached for later use and is empty.
-            PROCESS_STATE_CACHED_EMPTY = 16;
-            // Process does not exist.
-            PROCESS_STATE_NONEXISTENT = 17;
-        }
+    // A com.android.server.power.PowerManagerService.UidState object.
+    message UidStateProto {
         optional int32 uid = 1;
         optional string uid_string = 2;
         optional bool is_active = 3;
         optional int32 num_wake_locks = 4;
         optional bool is_process_state_unknown = 5;
-        optional ProcessState process_state = 6;
-    }
-
-    // Enum values gotten from PowerManagerInternal.java
-    enum Wakefulness {
-        WAKEFULNESS_ASLEEP = 0;
-        WAKEFULNESS_AWAKE = 1;
-        WAKEFULNESS_DREAMING = 2;
-        WAKEFULNESS_DOZING = 3;
-        WAKEFULNESS_UNKNOWN = 4;
-    }
-    // Enum values gotten from BatteryManager.java
-    enum PlugType {
-        PLUG_TYPE_NONE = 0;
-        PLUG_TYPE_PLUGGED_AC = 1;
-        PLUG_TYPE_PLUGGED_USB = 2;
-        PLUG_TYPE_PLUGGED_WIRELESS = 4;
-    }
-    // Enum values gotten from Intent.java
-    enum DockState {
-        DOCK_STATE_UNDOCKED = 0;
-        DOCK_STATE_DESK = 1;
-        DOCK_STATE_CAR = 2;
-        DOCK_STATE_LE_DESK = 3;
-        DOCK_STATE_HE_DESK = 4;
+        optional .android.app.ActivityManagerProto.ProcessState process_state = 6;
     }
 
     optional ConstantsProto constants = 1;
@@ -124,18 +65,18 @@
     // changed and need to be recalculated.
     optional int32 dirty = 2;
     // Indicates whether the device is awake or asleep or somewhere in between.
-    optional Wakefulness wakefulness = 3;
+    optional .android.os.PowerManagerInternalProto.Wakefulness wakefulness = 3;
     optional bool is_wakefulness_changing = 4;
     // True if the device is plugged into a power source.
     optional bool is_powered = 5;
     // The current plug type
-    optional PlugType plug_type = 6;
+    optional .android.os.BatteryManagerProto.PlugType plug_type = 6;
     // The current battery level percentage.
     optional int32 battery_level = 7;
     // The battery level percentage at the time the dream started.
     optional int32 battery_level_when_dream_started = 8;
     // The current dock state.
-    optional DockState dock_state = 9;
+    optional .android.content.IntentProto.DockState dock_state = 9;
     // True if the device should stay on.
     optional bool is_stay_on = 10;
     // True if the proximity sensor reads a positive result.
@@ -215,8 +156,8 @@
     // Some uids have actually changed while mUidsChanging was true.
     optional bool are_uids_changed = 45;
     // List of UIDs and their states
-    repeated UidProto uids = 46;
-    optional android.os.LooperProto looper = 47;
+    repeated UidStateProto uid_states = 46;
+    optional .android.os.LooperProto looper = 47;
     // List of all wake locks acquired by applications.
     repeated WakeLockProto wake_locks = 48;
     // List of all suspend blockers.
@@ -224,11 +165,13 @@
     optional WirelessChargerDetectorProto wireless_charger_detector = 50;
 }
 
+// A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
 message SuspendBlockerProto {
     optional string name = 1;
     optional int32 reference_count = 2;
 }
 
+// A com.android.server.power.PowerManagerService.WakeLock object.
 message WakeLockProto {
     message WakeLockFlagsProto {
         // Turn the screen on when the wake lock is acquired.
@@ -238,27 +181,7 @@
         optional bool is_on_after_release = 2;
     }
 
-    // Enum values gotten from PowerManager.java
-    enum LockLevel {
-        WAKE_LOCK_INVALID = 0;
-        // Ensures that the CPU is running.
-        PARTIAL_WAKE_LOCK = 1;
-        // Ensures that the screen is on (but may be dimmed).
-        SCREEN_DIM_WAKE_LOCK = 6;
-        // Ensures that the screen is on at full brightness.
-        SCREEN_BRIGHT_WAKE_LOCK = 10;
-        // Ensures that the screen and keyboard backlight are on at full brightness.
-        FULL_WAKE_LOCK = 26;
-        // Turns the screen off when the proximity sensor activates.
-        PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32;
-        // Put the screen in a low power state and allow the CPU to suspend
-        // if no other wake locks are held.
-        DOZE_WAKE_LOCK = 64;
-        // Keep the device awake enough to allow drawing to occur.
-        DRAW_WAKE_LOCK = 128;
-    }
-
-    optional LockLevel lock_level = 1;
+    optional .android.os.PowerManagerProto.WakeLockLevel lock_level = 1;
     optional string tag = 2;
     optional WakeLockFlagsProto flags = 3;
     optional bool is_disabled = 4;
@@ -269,7 +192,7 @@
     optional int32 uid = 7;
     // Owner PID
     optional int32 pid = 8;
-    optional android.os.WorkSourceProto work_source = 9;
+    optional .android.os.WorkSourceProto work_source = 9;
 }
 
 message PowerServiceSettingsAndConfigurationDumpProto {
@@ -285,22 +208,6 @@
         optional int32 setting_for_vr_default = 4;
     }
 
-    // Enum values gotten from Settings.java
-    enum ScreenBrightnessMode {
-        SCREEN_BRIGHTNESS_MODE_MANUAL = 0;
-        SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
-    }
-    // Enum values gotten from Display.java
-    enum DisplayState {
-        DISPLAY_STATE_UNKNOWN = 0;
-        DISPLAY_STATE_OFF = 1;
-        DISPLAY_STATE_ON = 2;
-        DISPLAY_STATE_DOZE = 3;
-        DISPLAY_STATE_DOZE_SUSPEND = 4;
-        DISPLAY_STATE_VR = 5;
-    }
-
-
     // True to decouple auto-suspend mode from the display state.
     optional bool is_decouple_hal_auto_suspend_mode_from_display_config = 1;
     // True to decouple interactive mode from the display state.
@@ -371,7 +278,7 @@
     // Use 0 if there is no adjustment.
     optional float screen_auto_brightness_adjustment_setting = 31;
     // The screen brightness mode.
-    optional ScreenBrightnessMode screen_brightness_mode_setting = 32;
+    optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 32;
     // The screen brightness setting override from the window manager
     // to allow the current foreground activity to override the brightness.
     // Use -1 to disable.
@@ -393,7 +300,7 @@
     // Use NaN to disable.
     optional float temporary_screen_auto_brightness_adjustment_setting_override = 37;
     // The screen state to use while dozing.
-    optional DisplayState doze_screen_state_override_from_dream_manager = 38;
+    optional .android.view.DisplayProto.DisplayState doze_screen_state_override_from_dream_manager = 38;
     // The screen brightness to use while dozing.
     optional float dozed_screen_brightness_override_from_dream_manager = 39;
     // Screen brightness settings limits.
diff --git a/core/proto/android/service/wirelesschargerdetector.proto b/core/proto/android/server/wirelesschargerdetector.proto
similarity index 97%
rename from core/proto/android/service/wirelesschargerdetector.proto
rename to core/proto/android/server/wirelesschargerdetector.proto
index bd697c8..89cf2f8 100644
--- a/core/proto/android/service/wirelesschargerdetector.proto
+++ b/core/proto/android/server/wirelesschargerdetector.proto
@@ -15,7 +15,7 @@
  */
 
 syntax = "proto2";
-package android.service.power;
+package com.android.server.power;
 
 option java_multiple_files = true;
 
@@ -46,4 +46,4 @@
     optional VectorProto first_sample = 9;
     // The value of the last sample that was collected.
     optional VectorProto last_sample = 10;
-}
\ No newline at end of file
+}
diff --git a/core/proto/android/service/battery.proto b/core/proto/android/service/battery.proto
index 998a808..8382b82 100644
--- a/core/proto/android/service/battery.proto
+++ b/core/proto/android/service/battery.proto
@@ -20,13 +20,9 @@
 option java_multiple_files = true;
 option java_outer_classname = "BatteryServiceProto";
 
+import "frameworks/base/core/proto/android/os/batterymanager.proto";
+
 message BatteryServiceDumpProto {
-    enum BatteryPlugged {
-        BATTERY_PLUGGED_NONE = 0;
-        BATTERY_PLUGGED_AC = 1;
-        BATTERY_PLUGGED_USB = 2;
-        BATTERY_PLUGGED_WIRELESS = 4;
-    }
     enum BatteryStatus {
         BATTERY_STATUS_INVALID = 0;
         BATTERY_STATUS_UNKNOWN = 1;
@@ -49,7 +45,7 @@
     // If true: UPDATES STOPPED -- use 'reset' to restart
     optional bool are_updates_stopped = 1;
     // Plugged status of power sources
-    optional BatteryPlugged plugged = 2;
+    optional android.os.BatteryManagerProto.PlugType plugged = 2;
     // Max current in microamperes
     optional int32 max_charging_current = 3;
     // Max voltage
diff --git a/core/proto/android/view/display.proto b/core/proto/android/view/display.proto
new file mode 100644
index 0000000..210c6d1
--- /dev/null
+++ b/core/proto/android/view/display.proto
@@ -0,0 +1,41 @@
+/*
+ * 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";
+package android.view;
+
+option java_multiple_files = true;
+
+message DisplayProto {
+    enum DisplayState {
+        // The display state is unknown.
+        DISPLAY_STATE_UNKNOWN = 0;
+        // The display state is off.
+        DISPLAY_STATE_OFF = 1;
+        // The display state is on.
+        DISPLAY_STATE_ON = 2;
+        // The display is dozing in a low power state; it is still on but is
+        // optimized for showing system-provided content while the device is
+        // non-interactive.
+        DISPLAY_STATE_DOZE = 3;
+        // The display is dozing in a suspended low power state; it is still on
+        // but is optimized for showing static system-provided content while the
+        // device is non-interactive.
+        DISPLAY_STATE_DOZE_SUSPEND = 4;
+        // The display is on and optimized for VR mode.
+        DISPLAY_STATE_VR = 5;
+    }
+}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0e90287..be0f6d9 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1334,7 +1334,7 @@
          split that contains the defined component. -->
     <attr name="splitName" format="string" />
 
-    <!-- Specifies the target sandbox this app wants to use. Higher sanbox versions
+    <!-- Specifies the target sandbox this app wants to use. Higher sandbox versions
          will have increasing levels of security.
 
          <p>The default value of this attribute is <code>1</code>. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 571aad9..e5f3aaf 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2356,6 +2356,10 @@
          property. If this is false, then the following recents config flags are ignored. -->
     <bool name="config_hasRecents">true</bool>
 
+    <!-- Component name for the activity that will be presenting the Recents UI, which will receive
+         special permissions for API related to fetching and presenting recent tasks. -->
+    <string name="config_recentsComponentName" translatable="false">com.android.systemui/.recents.RecentsActivity</string>
+
     <!-- The minimum number of visible recent tasks to be presented to the user through the
          SystemUI. Can be -1 if there is no minimum limit. -->
     <integer name="config_minNumVisibleRecentTasks_grid">-1</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 896de53..3dff9d7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -311,6 +311,7 @@
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
   <java-symbol type="bool" name="config_hasRecents" />
+  <java-symbol type="string" name="config_recentsComponentName" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
   <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index 25d247d..671c57c 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -332,6 +332,9 @@
     /**
      * Gets the clock's Drift in nanoseconds per second.
      *
+     * <p>This value is the instantaneous time-derivative of the value provided by
+     * {@link #getBiasNanos()}.
+     *
      * <p>A positive value indicates that the frequency is higher than the nominal (e.g. GPS master
      * clock) frequency. The error estimate for this reported drift is
      * {@link #getDriftUncertaintyNanosPerSecond()}.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 312b990..c92562b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -599,8 +599,8 @@
     private boolean isSettingsShown() {
         List<RunningTaskInfo> runningTasks;
         try {
-            runningTasks = mActivityManager.getTasks(1, 0);
-            if (runningTasks == null || runningTasks.size() == 0) {
+            runningTasks = mActivityManager.getTasks(1);
+            if (runningTasks.isEmpty()) {
                 return false;
             }
         } catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 87f24fd..55ec5e7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -200,25 +200,17 @@
      */
     public ActivityManager.RunningTaskInfo getRunningTask() {
         // Note: The set of running tasks from the system is ordered by recency
-        List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(10);
-        if (tasks == null || tasks.isEmpty()) {
+        try {
+            List<ActivityManager.RunningTaskInfo> tasks = mIam.getFilteredTasks(1,
+                    ACTIVITY_TYPE_RECENTS /* ignoreActivityType */,
+                    WINDOWING_MODE_PINNED /* ignoreWindowingMode */);
+            if (tasks.isEmpty()) {
+                return null;
+            }
+            return tasks.get(0);
+        } catch (RemoteException e) {
             return null;
         }
-
-        // Find the first task in a valid stack, we ignore everything from the Recents and PiP
-        // stacks
-        for (int i = 0; i < tasks.size(); i++) {
-            final ActivityManager.RunningTaskInfo task = tasks.get(i);
-            final WindowConfiguration winConfig = task.configuration.windowConfiguration;
-            if (winConfig.getActivityType() == ACTIVITY_TYPE_RECENTS) {
-                continue;
-            }
-            if (winConfig.getWindowingMode() == WINDOWING_MODE_PINNED) {
-                continue;
-            }
-            return task;
-        }
-        return null;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 25c2fc9..7442904 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -302,11 +302,6 @@
      */
     private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
             final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
-        if (activityType == ACTIVITY_TYPE_RECENTS || activityType == ACTIVITY_TYPE_HOME
-                || windowingMode == WINDOWING_MODE_PINNED) {
-            return null;
-        }
-
         // Calculate the offscreen task rect (for tasks that are not backed by views)
         TaskView taskView = stackView.getChildViewForTask(task);
         TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 68fe9a8..84b7015 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -29,7 +29,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewAnimationUtils;
-import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
@@ -173,12 +172,12 @@
     private int mOverrideTint;
     private float mOverrideAmount;
     private boolean mShadowHidden;
-    private boolean mWasActivatedOnDown;
     /**
      * Similar to mDimmed but is also true if it's not dimmable but should be
      */
     private boolean mNeedsDimming;
     private int mDimmedAlpha;
+    private boolean mBlockNextTouch;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -204,7 +203,7 @@
             } else {
                 makeInactive(true /* animate */);
             }
-        }, this::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
+        }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
     }
 
     @Override
@@ -241,9 +240,15 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mNeedsDimming && !mActivated && ev.getActionMasked() == MotionEvent.ACTION_DOWN
+        if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
                 && disallowSingleClick(ev) && !isTouchExplorationEnabled()) {
-            return true;
+            if (!mActivated) {
+                return true;
+            } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
+                mBlockNextTouch = true;
+                makeInactive(true /* animate */);
+                return true;
+            }
         }
         return super.onInterceptTouchEvent(ev);
     }
@@ -263,10 +268,11 @@
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         boolean result;
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            mWasActivatedOnDown = mActivated;
+        if (mBlockNextTouch) {
+            mBlockNextTouch = false;
+            return false;
         }
-        if ((mNeedsDimming && !mActivated) && !isTouchExplorationEnabled() && isInteractive()) {
+        if (mNeedsDimming && !isTouchExplorationEnabled() && isInteractive()) {
             boolean wasActivated = mActivated;
             result = handleTouchEventDimmed(event);
             if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
@@ -312,7 +318,7 @@
 
     @Override
     public boolean performClick() {
-        if (mWasActivatedOnDown || !mNeedsDimming || isTouchExplorationEnabled()) {
+        if (!mNeedsDimming || isTouchExplorationEnabled()) {
             return super.performClick();
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
index dcb6a38..0d62703 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
@@ -142,7 +142,7 @@
                 && Math.abs(event.getY() - mDownY) < mTouchSlop;
     }
 
-    private boolean isWithinDoubleTapSlop(MotionEvent event) {
+    public boolean isWithinDoubleTapSlop(MotionEvent event) {
         if (!mActivated) {
             // If we're not activated there's no double tap slop to satisfy.
             return true;
diff --git a/proto/Android.bp b/proto/Android.bp
new file mode 100644
index 0000000..95f453c
--- /dev/null
+++ b/proto/Android.bp
@@ -0,0 +1,17 @@
+java_library_static {
+    name: "framework-protos",
+    host_supported: true,
+    proto: {
+        type: "nano",
+    },
+    srcs: ["src/**/*.proto"],
+    no_framework_libs: true,
+    target: {
+        android: {
+            jarjar_rules: "jarjar-rules.txt",
+        },
+        host: {
+            static_libs: ["libprotobuf-java-nano"],
+        },
+    },
+}
diff --git a/proto/Android.mk b/proto/Android.mk
deleted file mode 100644
index 1c03d16..0000000
--- a/proto/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := framework-protos
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_SRC_FILES:= $(call all-proto-files-under, src)
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
-
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/jarjar-rules.txt
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Host-side version of framework-protos
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := host-framework-protos
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_SRC_FILES:= $(call all-proto-files-under, src)
-
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := host-libprotobuf-java-nano
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/jarjar-rules.txt
-
-include $(BUILD_HOST_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 5dd3620..93aa520 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4704,6 +4704,11 @@
     // OS: P
     AUTOFILL_SAVE_EXPLICITLY_TRIGGERED = 1229;
 
+    // OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling
+    // CATEGORY: SETTINGS
+    // OS: P
+    WIFI_CALLING_FOR_SUB = 1230;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f9213aa..622b8423 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2877,7 +2877,7 @@
                     // The backend reports that our dataset has been wiped.  Note this in
                     // the event log; the no-success code below will reset the backup
                     // state as well.
-                    EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
+                    EventLog.writeEvent(EventLogTags.BACKUP_RESET, transportName);
                 }
             } catch (Exception e) {
                 Slog.e(TAG, "Error in backup thread", e);
@@ -9781,7 +9781,8 @@
                     }
 
                     Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
-                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
+                    String transportDirName = transport.transportDirName();
+                    EventLog.writeEvent(EventLogTags.BACKUP_START, transportDirName);
                     long startRealtime = SystemClock.elapsedRealtime();
                     int status = transport.initializeDevice();
 
@@ -9794,7 +9795,7 @@
                         Slog.i(TAG, "Device init successful");
                         int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
                         EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
-                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
+                        resetBackupState(new File(mBaseStateDir, transportDirName));
                         EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
                         synchronized (mQueueLock) {
                             recordInitPendingLocked(false, transportName);
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index 7a8a920e..c0caa557 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -339,7 +339,7 @@
                 // The backend reports that our dataset has been wiped.  Note this in
                 // the event log; the no-success code below will reset the backup
                 // state as well.
-                EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
+                EventLog.writeEvent(EventLogTags.BACKUP_RESET, transportName);
             }
         } catch (Exception e) {
             Slog.e(TAG, "Error in backup thread", e);
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 939b1ae..690922f 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -79,7 +79,8 @@
                 }
 
                 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
-                EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
+                String transportDirName = transport.transportDirName();
+                EventLog.writeEvent(EventLogTags.BACKUP_START, transportDirName);
                 long startRealtime = SystemClock.elapsedRealtime();
                 int status = transport.initializeDevice();
 
@@ -94,7 +95,7 @@
                     EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
                     backupManagerService
                             .resetBackupState(new File(backupManagerService.getBaseStateDir(),
-                                    transport.transportDirName()));
+                                    transportDirName));
                     EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
                     synchronized (backupManagerService.getQueueLock()) {
                         backupManagerService.recordInitPendingLocked(false, transportName);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index e7cf041..b824fab 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -43,6 +43,7 @@
 import android.hardware.health.V2_0.IHealth;
 import android.hardware.health.V2_0.Result;
 import android.os.BatteryManager;
+import android.os.BatteryManagerProto;
 import android.os.BatteryManagerInternal;
 import android.os.BatteryProperty;
 import android.os.Binder;
@@ -913,13 +914,13 @@
 
         synchronized (mLock) {
             proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
-            int batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_NONE;
+            int batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_NONE;
             if (mHealthInfo.legacy.chargerAcOnline) {
-                batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_AC;
+                batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_AC;
             } else if (mHealthInfo.legacy.chargerUsbOnline) {
-                batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_USB;
+                batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_USB;
             } else if (mHealthInfo.legacy.chargerWirelessOnline) {
-                batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_WIRELESS;
+                batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_WIRELESS;
             }
             proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
             proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 348c799..d819a33d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -70,7 +70,6 @@
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.Uri;
-import android.net.metrics.DefaultNetworkEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.util.MultinetworkPolicyTracker;
@@ -127,6 +126,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
 import com.android.server.connectivity.MockableSystemProperties;
@@ -2265,7 +2265,7 @@
                 // Let rematchAllNetworksAndRequests() below record a new default network event
                 // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
                 // whose timestamps tell how long it takes to recover a default network.
-                logDefaultNetworkEvent(null, nai);
+                metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(null, nai);
             }
             notifyIfacesChangedForNetworkStats();
             // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -4995,7 +4995,8 @@
             // Notify system services that this network is up.
             makeDefault(newNetwork);
             // Log 0 -> X and Y -> X default network transitions, where X is the new default.
-            logDefaultNetworkEvent(newNetwork, oldDefaultNetwork);
+            metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
+                    newNetwork, oldDefaultNetwork);
             // Have a new default network, release the transition wakelock in
             scheduleReleaseNetworkTransitionWakelock();
         }
@@ -5554,25 +5555,10 @@
         return ServiceManager.checkService(name) != null;
     }
 
-    private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
-        int newNetid = NETID_UNSET;
-        int prevNetid = NETID_UNSET;
-        int[] transports = new int[0];
-        boolean hadIPv4 = false;
-        boolean hadIPv6 = false;
-
-        if (newNai != null) {
-            newNetid = newNai.network.netId;
-            transports = newNai.networkCapabilities.getTransportTypes();
-        }
-        if (prevNai != null) {
-            prevNetid = prevNai.network.netId;
-            final LinkProperties lp = prevNai.linkProperties;
-            hadIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute();
-            hadIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
-        }
-
-        mMetricsLog.log(new DefaultNetworkEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6));
+    @VisibleForTesting
+    protected IpConnectivityMetrics.Logger metricsLogger() {
+        return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
+                "no IpConnectivityMetrics service");
     }
 
     private void logNetworkEvent(NetworkAgentInfo nai, int evtype) {
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 29073cb..c04ddf8 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -604,7 +604,7 @@
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         proto.write(ID, mDisplayId);
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a513e14..93c0a395 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -23,17 +23,20 @@
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
+import static android.Manifest.permission.REMOVE_TASKS;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
@@ -227,6 +230,8 @@
 import android.app.ProfilerInfo;
 import android.app.RemoteAction;
 import android.app.WaitResult;
+import android.app.WindowConfiguration.ActivityType;
+import android.app.WindowConfiguration.WindowingMode;
 import android.app.admin.DevicePolicyManager;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
@@ -668,7 +673,7 @@
     /**
      * List of intents that were used to start the most recent tasks.
      */
-    final RecentTasks mRecentTasks;
+    private final RecentTasks mRecentTasks;
 
     /**
      * For addAppTask: cached of the last activity component that was added.
@@ -2033,7 +2038,9 @@
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
                         ProcessRecord r = mLruProcesses.get(i);
-                        if (r.thread != null) {
+                        // Don't dispatch to isolated processes as they can't access
+                        // ConnectivityManager and don't have network privileges anyway.
+                        if (r.thread != null && !r.isolated) {
                             try {
                                 r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
                             } catch (RemoteException ex) {
@@ -2767,7 +2774,7 @@
         mTaskChangeNotificationController =
                 new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
         mActivityStarter = new ActivityStarter(this);
-        mRecentTasks = new RecentTasks(this, mStackSupervisor);
+        mRecentTasks = createRecentTasks();
         mStackSupervisor.setRecentTasks(mRecentTasks);
         mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
 
@@ -2813,6 +2820,14 @@
         return new ActivityStackSupervisor(this, mHandler.getLooper());
     }
 
+    protected RecentTasks createRecentTasks() {
+        return new RecentTasks(this, mStackSupervisor);
+    }
+
+    RecentTasks getRecentTasks() {
+        return mRecentTasks;
+    }
+
     public void setSystemServiceManager(SystemServiceManager mgr) {
         mSystemServiceManager = mgr;
     }
@@ -3148,6 +3163,7 @@
             synchronized (this) {
                 final ActivityStack stack = mStackSupervisor.getStack(stackId);
                 if (stack == null) {
+                    Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
                     return;
                 }
                 final ActivityRecord r = stack.topRunningActivityLocked();
@@ -3184,7 +3200,8 @@
     /** Sets the task stack listener that gets callbacks when a task stack changes. */
     @Override
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "registerTaskStackListener()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "registerTaskStackListener()");
         mTaskChangeNotificationController.registerTaskStackListener(listener);
     }
 
@@ -3193,7 +3210,8 @@
      */
     @Override
     public void unregisterTaskStackListener(ITaskStackListener listener) throws RemoteException {
-         enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "unregisterTaskStackListener()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "unregisterTaskStackListener()");
          mTaskChangeNotificationController.unregisterTaskStackListener(listener);
      }
 
@@ -4871,12 +4889,9 @@
 
     @Override
     public final int startActivityFromRecents(int taskId, Bundle bOptions) {
-        if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) {
-            String msg = "Permission Denial: startActivityFromRecents called without " +
-                    START_TASKS_FROM_RECENTS;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
+        enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+                "startActivityFromRecents()");
+
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -8328,9 +8343,6 @@
         }
     }
 
-    /**
-     * This can be called with or without the global lock held.
-     */
     int checkComponentPermission(String permission, int pid, int uid,
             int owningUid, boolean exported) {
         if (pid == MY_PID) {
@@ -8405,6 +8417,15 @@
     }
 
     /**
+     * This can be called with or without the global lock held.
+     */
+    void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+        if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
+            enforceCallingPermission(permission, func);
+        }
+    }
+
+    /**
      * Determine if UID is holding permissions required to access {@link Uri} in
      * the given {@link ProviderInfo}. Final permission checking is always done
      * in {@link ContentProvider}.
@@ -9770,25 +9791,34 @@
     }
 
     @Override
-    public List<RunningTaskInfo> getTasks(int maxNum, int flags) {
+    public List<RunningTaskInfo> getTasks(int maxNum) {
+       return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
+    }
+
+    @Override
+    public List<RunningTaskInfo> getFilteredTasks(int maxNum, @ActivityType int ignoreActivityType,
+            @WindowingMode int ignoreWindowingMode) {
         final int callingUid = Binder.getCallingUid();
-        ArrayList<RunningTaskInfo> list = new ArrayList<RunningTaskInfo>();
+        ArrayList<RunningTaskInfo> list = new ArrayList<>();
 
         synchronized(this) {
-            if (DEBUG_ALL) Slog.v(
-                TAG, "getTasks: max=" + maxNum + ", flags=" + flags);
+            if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
 
             final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
                     callingUid);
-
-            // TODO: Improve with MRU list from all ActivityStacks.
-            mStackSupervisor.getTasksLocked(maxNum, list, callingUid, allowed);
+            mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
+                    ignoreWindowingMode, callingUid, allowed);
         }
 
         return list;
     }
 
     private boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+        if (mRecentTasks.isCallerRecents(callingUid)) {
+            // Always allow the recents component to get tasks
+            return true;
+        }
+
         boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS,
                 callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
         if (!allowed) {
@@ -9836,8 +9866,7 @@
     @Override
     public ActivityManager.TaskDescription getTaskDescription(int id) {
         synchronized (this) {
-            enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                    "getTaskDescription()");
+            enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
             final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
                     MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
             if (tr != null) {
@@ -10031,7 +10060,8 @@
 
     @Override
     public void cancelTaskWindowTransition(int taskId) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "cancelTaskWindowTransition()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "cancelTaskWindowTransition()");
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -10050,7 +10080,8 @@
 
     @Override
     public void cancelTaskThumbnailTransition(int taskId) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "cancelTaskThumbnailTransition()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "cancelTaskThumbnailTransition()");
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -10069,7 +10100,7 @@
 
     @Override
     public TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
-        enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
+        enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
         final long ident = Binder.clearCallingIdentity();
         try {
             final TaskRecord task;
@@ -10122,12 +10153,13 @@
 
     @Override
     public void removeStack(int stackId) {
-        enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
                 final ActivityStack stack = mStackSupervisor.getStack(stackId);
                 if (stack == null) {
+                    Slog.w(TAG, "removeStack: No stack with id=" + stackId);
                     return;
                 }
                 if (!stack.isActivityTypeStandardOrUndefined()) {
@@ -10147,7 +10179,8 @@
      */
     @Override
     public void removeStacksInWindowingModes(int[] windowingModes) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksInWindowingModes()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "removeStacksInWindowingModes()");
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -10160,7 +10193,8 @@
 
     @Override
     public void removeStacksWithActivityTypes(int[] activityTypes) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksWithActivityTypes()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "removeStacksWithActivityTypes()");
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -10189,7 +10223,7 @@
 
     @Override
     public boolean removeTask(int taskId) {
-        enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, "removeTask()");
+        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -10366,7 +10400,7 @@
 
     @Override
     public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -10402,7 +10436,7 @@
 
     @Override
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
@@ -10453,7 +10487,7 @@
     @Override
     public boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
             Rect initialBounds) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()");
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
@@ -10492,12 +10526,16 @@
      */
     @Override
     public void dismissSplitScreenMode(boolean toTop) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
                 final ActivityStack stack =
                         mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                if (stack == null) {
+                    Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
+                    return;
+                }
                 if (toTop) {
                     mStackSupervisor.resizeStackLocked(stack, null /* destBounds */,
                             null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
@@ -10520,14 +10558,14 @@
      */
     @Override
     public void dismissPip(boolean animate, int animationDuration) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
                 final PinnedActivityStack stack =
                         mStackSupervisor.getDefaultDisplay().getPinnedStack();
-
                 if (stack == null) {
+                    Slog.w(TAG, "dismissPip: pinned stack not found.");
                     return;
                 }
                 if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
@@ -10557,7 +10595,8 @@
      */
     @Override
     public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTopActivityToPinnedStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "moveTopActivityToPinnedStack()");
         synchronized (this) {
             if (!mSupportsPictureInPicture) {
                 throw new IllegalStateException("moveTopActivityToPinnedStack:"
@@ -10576,13 +10615,14 @@
     @Override
     public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
             boolean preserveWindows, boolean animate, int animationDuration) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
                 if (animate) {
                     final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
                     if (stack == null) {
+                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
                         return;
                     }
                     if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
@@ -10611,8 +10651,7 @@
     public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds,
             Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "resizeDockedStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -10627,8 +10666,7 @@
 
     @Override
     public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "resizePinnedStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -10687,7 +10725,7 @@
 
     @Override
     public List<StackInfo> getAllStackInfos() {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -10700,7 +10738,7 @@
 
     @Override
     public StackInfo getStackInfo(int windowingMode, int activityType) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -11861,8 +11899,7 @@
 
     @Override
     public void appNotRespondingViaProvider(IBinder connection) {
-        enforceCallingPermission(
-                android.Manifest.permission.REMOVE_TASKS, "appNotRespondingViaProvider()");
+        enforceCallingPermission(REMOVE_TASKS, "appNotRespondingViaProvider()");
 
         final ContentProviderConnection conn = (ContentProviderConnection) connection;
         if (conn == null) {
@@ -18125,8 +18162,7 @@
     // =========================================================
 
     @Override
-    public List<ActivityManager.RunningServiceInfo> getServices(int maxNum,
-            int flags) {
+    public List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags) {
         enforceNotIsolatedCaller("getServices");
 
         final int callingUid = Binder.getCallingUid();
@@ -20070,7 +20106,7 @@
 
     @Override
     public StackInfo getFocusedStackInfo() throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -20110,7 +20146,8 @@
     @Override
     // TODO: API should just be about changing windowing modes...
     public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTasksToFullscreenStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "moveTasksToFullscreenStack()");
         synchronized (this) {
             final long origId = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 4cf2794..f942265 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2305,7 +2305,7 @@
     int runWrite(PrintWriter pw) {
         mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
                 "registerUidObserver()");
-        mInternal.mRecentTasks.flush();
+        mInternal.getRecentTasks().flush();
         pw.println("All tasks persisted.");
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b47f819..2f0b649 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -204,7 +204,6 @@
     private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     private static final boolean SHOW_ACTIVITY_START_TIME = true;
-    private static final String RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
 
     private static final String ATTR_ID = "id";
     private static final String TAG_INTENT = "intent";
@@ -1058,7 +1057,7 @@
                 // We only allow home activities to be resizeable if they explicitly requested it.
                 info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
             }
-        } else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) {
+        } else if (service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
             activityType = ACTIVITY_TYPE_RECENTS;
         } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
                 && canLaunchAssistActivity(launchedFromPackage)) {
@@ -1562,17 +1561,7 @@
             return false;
         }
 
-        boolean isVisible = !behindFullscreenActivity || mLaunchTaskBehind;
-
-        if (service.mSupportsLeanbackOnly && isVisible && isActivityTypeRecents()) {
-            // On devices that support leanback only (Android TV), Recents activity can only be
-            // visible if the home stack is the focused stack or we are in split-screen mode.
-            final ActivityDisplay display = getDisplay();
-            boolean hasSplitScreenStack = display != null && display.hasSplitScreenPrimaryStack();
-            isVisible = hasSplitScreenStack || mStackSupervisor.isFocusedStack(getStack());
-        }
-
-        return isVisible;
+        return !behindFullscreenActivity || mLaunchTaskBehind;
     }
 
     void makeVisibleIfNeeded(ActivityRecord starting) {
@@ -2074,7 +2063,7 @@
             final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId),
                     iconFilename);
             final String iconFilePath = iconFile.getAbsolutePath();
-            service.mRecentTasks.saveImage(icon, iconFilePath);
+            service.getRecentTasks().saveImage(icon, iconFilePath);
             _taskDescription.setIconFilename(iconFilePath);
         }
         taskDescription = _taskDescription;
@@ -2821,7 +2810,7 @@
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         writeIdentifierToProto(proto, IDENTIFIER);
         proto.write(STATE, state.toString());
         proto.write(VISIBLE, visible);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index bafcce7..ba41bd4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -99,6 +99,8 @@
 import android.app.AppGlobals;
 import android.app.IActivityController;
 import android.app.ResultInfo;
+import android.app.WindowConfiguration.ActivityType;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -4605,69 +4607,43 @@
         return didSomething;
     }
 
-    void getTasksLocked(List<RunningTaskInfo> list, int callingUid, boolean allowed) {
+    /**
+     * @return The set of running tasks through {@param tasksOut} that are available to the caller.
+     *         If {@param ignoreActivityType} or {@param ignoreWindowingMode} are not undefined,
+     *         then skip running tasks that match those types.
+     */
+    void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
+            @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
         boolean focusedStack = mStackSupervisor.getFocusedStack() == this;
         boolean topTask = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             if (task.getTopActivity() == null) {
+                // Skip if there are no activities in the task
                 continue;
             }
-            ActivityRecord r = null;
-            ActivityRecord top = null;
-            ActivityRecord tmp;
-            int numActivities = 0;
-            int numRunning = 0;
-            final ArrayList<ActivityRecord> activities = task.mActivities;
             if (!allowed && !task.isActivityTypeHome() && task.effectiveUid != callingUid) {
+                // Skip if the caller can't fetch this task
                 continue;
             }
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                tmp = activities.get(activityNdx);
-                if (tmp.finishing) {
-                    continue;
-                }
-                r = tmp;
-
-                // Initialize state for next task if needed.
-                if (top == null || (top.state == ActivityState.INITIALIZING)) {
-                    top = r;
-                    numActivities = numRunning = 0;
-                }
-
-                // Add 'r' into the current task.
-                numActivities++;
-                if (r.app != null && r.app.thread != null) {
-                    numRunning++;
-                }
-
-                if (DEBUG_ALL) Slog.v(
-                    TAG, r.intent.getComponent().flattenToShortString()
-                    + ": task=" + r.getTask());
+            if (ignoreActivityType != ACTIVITY_TYPE_UNDEFINED
+                    && task.getActivityType() == ignoreActivityType) {
+                // Skip ignored activity type
+                continue;
             }
-
-            RunningTaskInfo ci = new RunningTaskInfo();
-            ci.id = task.taskId;
-            ci.stackId = mStackId;
-            ci.baseActivity = r.intent.getComponent();
-            ci.topActivity = top.intent.getComponent();
-            ci.lastActiveTime = task.lastActiveTime;
+            if (ignoreWindowingMode != WINDOWING_MODE_UNDEFINED
+                    && task.getWindowingMode() == ignoreWindowingMode) {
+                // Skip ignored windowing mode
+                continue;
+            }
             if (focusedStack && topTask) {
-                // Give the latest time to ensure foreground task can be sorted
-                // at the first, because lastActiveTime of creating task is 0.
-                ci.lastActiveTime = SystemClock.elapsedRealtime();
+                // For the focused stack top task, update the last stack active time so that it can
+                // be used to determine the order of the tasks (it may not be set for newly created
+                // tasks)
+                task.lastActiveTime = SystemClock.elapsedRealtime();
                 topTask = false;
             }
-
-            if (top.getTask() != null) {
-                ci.description = top.getTask().lastDescription;
-            }
-            ci.numActivities = numActivities;
-            ci.numRunning = numRunning;
-            ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
-            ci.resizeMode = task.mResizeMode;
-            ci.configuration.setTo(task.getConfiguration());
-            list.add(ci);
+            tasksOut.add(task);
         }
     }
 
@@ -5023,7 +4999,7 @@
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         proto.write(ID, mStackId);
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 14c0eea..6ec158e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -110,6 +110,8 @@
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.WaitResult;
+import android.app.WindowConfiguration.ActivityType;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -279,8 +281,12 @@
 
     final ActivityManagerService mService;
 
+    /** The historial list of recent tasks including inactive tasks */
     RecentTasks mRecentTasks;
 
+    /** Helper class to abstract out logic for fetching the set of currently running tasks */
+    private RunningTasks mRunningTasks;
+
     final ActivityStackSupervisorHandler mHandler;
 
     /** Short cut */
@@ -566,6 +572,7 @@
     public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
         mService = service;
         mHandler = new ActivityStackSupervisorHandler(looper);
+        mRunningTasks = createRunningTasks();
         mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
         mKeyguardController = new KeyguardController(service, this);
 
@@ -578,6 +585,11 @@
         mRecentTasks.registerCallback(this);
     }
 
+    @VisibleForTesting
+    RunningTasks createRunningTasks() {
+        return new RunningTasks();
+    }
+
     /**
      * At the time when the constructor runs, the power manager has not yet been
      * initialized.  So we initialize our wakelocks afterwards.
@@ -1152,43 +1164,12 @@
         return null;
     }
 
-    void getTasksLocked(int maxNum, List<RunningTaskInfo> list, int callingUid, boolean allowed) {
-        // Gather all of the running tasks for each stack into runningTaskLists.
-        ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists = new ArrayList<>();
-        final int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<>();
-                runningTaskLists.add(stackTaskList);
-                stack.getTasksLocked(stackTaskList, callingUid, allowed);
-            }
-        }
-
-        // The lists are already sorted from most recent to oldest. Just pull the most recent off
-        // each list and add it to list. Stop when all lists are empty or maxNum reached.
-        while (maxNum > 0) {
-            long mostRecentActiveTime = Long.MIN_VALUE;
-            ArrayList<RunningTaskInfo> selectedStackList = null;
-            final int numTaskLists = runningTaskLists.size();
-            for (int stackNdx = 0; stackNdx < numTaskLists; ++stackNdx) {
-                ArrayList<RunningTaskInfo> stackTaskList = runningTaskLists.get(stackNdx);
-                if (!stackTaskList.isEmpty()) {
-                    final long lastActiveTime = stackTaskList.get(0).lastActiveTime;
-                    if (lastActiveTime > mostRecentActiveTime) {
-                        mostRecentActiveTime = lastActiveTime;
-                        selectedStackList = stackTaskList;
-                    }
-                }
-            }
-            if (selectedStackList != null) {
-                list.add(selectedStackList.remove(0));
-                --maxNum;
-            } else {
-                break;
-            }
-        }
+    @VisibleForTesting
+    void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
+            @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
+            int callingUid, boolean allowed) {
+        mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode,
+                mActivityDisplays, callingUid, allowed);
     }
 
     ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
@@ -1584,7 +1565,10 @@
             return false;
         }
         if (options != null) {
-            if (options.getLaunchTaskId() != INVALID_STACK_ID) {
+            // If a launch task id is specified, then ensure that the caller is the recents
+            // component or has the START_TASKS_FROM_RECENTS permission
+            if (options.getLaunchTaskId() != INVALID_TASK_ID
+                    && !mRecentTasks.isCallerRecents(callingUid)) {
                 final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
                         callingPid, callingUid);
                 if (startInTaskPerm == PERMISSION_DENIED) {
@@ -3692,7 +3676,7 @@
     }
 
     public void writeToProto(ProtoOutputStream proto) {
-        super.writeToProto(proto, CONFIGURATION_CONTAINER);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
             ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
             activityDisplay.writeToProto(proto, DISPLAYS);
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index 38b3039..17626ea 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -82,7 +82,7 @@
                 if (tr == null) {
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                 }
-                return RecentTasks.createRecentTaskInfo(tr);
+                return mService.getRecentTasks().createRecentTaskInfo(tr);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index ed3f503..0b9e0a2 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -37,8 +37,6 @@
 import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
-import com.google.android.collect.Sets;
-
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.content.ComponentName;
@@ -58,15 +56,16 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.ArraySet;
-import android.util.MutableBoolean;
-import android.util.MutableInt;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.am.TaskRecord.TaskActivitiesReport;
+
+import com.google.android.collect.Sets;
 
 import java.io.File;
 import java.io.PrintWriter;
@@ -75,7 +74,6 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -126,6 +124,13 @@
     private final UserController mUserController;
 
     /**
+     * Keeps track of the static recents package/component which is granted additional permissions
+     * to call recents-related APIs.
+     */
+    private int mRecentsUid = -1;
+    private ComponentName mRecentsComponent = null;
+
+    /**
      * Mapping of user id -> whether recent tasks have been loaded for that user.
      */
     private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
@@ -154,6 +159,7 @@
     private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
     private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
     private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
+    private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport();
 
     @VisibleForTesting
     RecentTasks(ActivityManagerService service, TaskPersister taskPersister,
@@ -173,7 +179,7 @@
         mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
         mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
         mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
-        loadParametersFromResources(service.mContext.getResources());
+        loadParametersFromResources(res);
     }
 
     @VisibleForTesting
@@ -217,6 +223,47 @@
                 : -1;
     }
 
+    /**
+     * Loads the static recents component.  This is called after the system is ready, but before
+     * any dependent services (like SystemUI) is started.
+     */
+    void loadRecentsComponent(Resources res) {
+        final String rawRecentsComponent = res.getString(
+                com.android.internal.R.string.config_recentsComponentName);
+        if (TextUtils.isEmpty(rawRecentsComponent)) {
+            return;
+        }
+
+        final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent);
+        if (cn != null) {
+            try {
+                final ApplicationInfo appInfo = AppGlobals.getPackageManager()
+                        .getApplicationInfo(cn.getPackageName(), 0, mService.mContext.getUserId());
+                if (appInfo != null) {
+                    mRecentsUid = appInfo.uid;
+                    mRecentsComponent = cn;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not load application info for recents component: " + cn);
+            }
+        }
+    }
+
+    /**
+     * @return whether the current caller has the same uid as the recents component.
+     */
+    boolean isCallerRecents(int callingUid) {
+        return UserHandle.isSameApp(callingUid, mRecentsUid);
+    }
+
+    /**
+     * @return whether the given component is the recents component and shares the same uid as the
+     *         recents component.
+     */
+    boolean isRecentsComponent(ComponentName cn, int uid) {
+        return cn.equals(mRecentsComponent) && UserHandle.isSameApp(uid, mRecentsUid);
+    }
+
     void registerCallback(Callbacks callback) {
         mCallbacks.add(callback);
     }
@@ -339,6 +386,7 @@
     }
 
     void onSystemReadyLocked() {
+        loadRecentsComponent(mService.mContext.getResources());
         mTasks.clear();
         mTaskPersister.startPersisting();
     }
@@ -690,7 +738,7 @@
                     continue;
                 }
 
-                ActivityManager.RecentTaskInfo rti = RecentTasks.createRecentTaskInfo(tr);
+                final ActivityManager.RecentTaskInfo rti = createRecentTaskInfo(tr);
                 if (!getDetailedTasks) {
                     rti.baseIntent.replaceExtras((Bundle)null);
                 }
@@ -1327,12 +1375,14 @@
 
     void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
         pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
+        pw.println("mRecentsUid=" + mRecentsUid);
+        pw.println("mRecentsComponent=" + mRecentsComponent);
         if (mTasks.isEmpty()) {
             return;
         }
 
-        final MutableBoolean printedAnything = new MutableBoolean(false);
-        final MutableBoolean printedHeader = new MutableBoolean(false);
+        boolean printedAnything = false;
+        boolean printedHeader = false;
         final int size = mTasks.size();
         for (int i = 0; i < size; i++) {
             final TaskRecord tr = mTasks.get(i);
@@ -1341,10 +1391,10 @@
                 continue;
             }
 
-            if (!printedHeader.value) {
+            if (!printedHeader) {
                 pw.println("  Recent tasks:");
-                printedHeader.value = true;
-                printedAnything.value = true;
+                printedHeader = true;
+                printedAnything = true;
             }
             pw.print("  * Recent #"); pw.print(i); pw.print(": ");
             pw.println(tr);
@@ -1353,7 +1403,7 @@
             }
         }
 
-        if (!printedAnything.value) {
+        if (!printedAnything) {
             pw.println("  (nothing)");
         }
     }
@@ -1361,7 +1411,7 @@
     /**
      * Creates a new RecentTaskInfo from a TaskRecord.
      */
-    static ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
+    ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
         // Update the task description to reflect any changes in the task stack
         tr.updateTaskDescription();
 
@@ -1387,24 +1437,10 @@
         rti.resizeMode = tr.mResizeMode;
         rti.configuration.setTo(tr.getConfiguration());
 
-        ActivityRecord base = null;
-        ActivityRecord top = null;
-        ActivityRecord tmp;
-
-        for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
-            tmp = tr.mActivities.get(i);
-            if (tmp.finishing) {
-                continue;
-            }
-            base = tmp;
-            if (top == null || (top.state == ActivityState.INITIALIZING)) {
-                top = base;
-            }
-            rti.numActivities++;
-        }
-
-        rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
-        rti.topActivity = (top != null) ? top.intent.getComponent() : null;
+        tr.getNumRunningActivities(mTmpReport);
+        rti.numActivities = mTmpReport.numActivities;
+        rti.baseActivity = (mTmpReport.base != null) ? mTmpReport.base.intent.getComponent() : null;
+        rti.topActivity = (mTmpReport.top != null) ? mTmpReport.top.intent.getComponent() : null;
 
         return rti;
     }
diff --git a/services/core/java/com/android/server/am/RunningTasks.java b/services/core/java/com/android/server/am/RunningTasks.java
new file mode 100644
index 0000000..400b03a
--- /dev/null
+++ b/services/core/java/com/android/server/am/RunningTasks.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration.ActivityType;
+import android.app.WindowConfiguration.WindowingMode;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * Class for resolving the set of running tasks in the system.
+ */
+class RunningTasks {
+
+    // Comparator to sort by last active time (descending)
+    private static final Comparator<TaskRecord> LAST_ACTIVE_TIME_COMPARATOR =
+            (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
+
+    private final TaskRecord.TaskActivitiesReport mTmpReport =
+            new TaskRecord.TaskActivitiesReport();
+    private final TreeSet<TaskRecord> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
+    private final ArrayList<TaskRecord> mTmpStackTasks = new ArrayList<>();
+
+    void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
+            @WindowingMode int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
+            int callingUid, boolean allowed) {
+        // For each stack on each display, add the tasks into the sorted set and then pull the first
+        // {@param maxNum} from the set
+
+        // Gather all of the tasks across all of the tasks, and add them to the sorted set
+        mTmpSortedSet.clear();
+        mTmpStackTasks.clear();
+        final int numDisplays = activityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = activityDisplays.valueAt(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
+                        callingUid, allowed);
+                for (int i = mTmpStackTasks.size() - 1; i >= 0; i--) {
+                    mTmpSortedSet.addAll(mTmpStackTasks);
+                }
+            }
+        }
+
+        // Take the first {@param maxNum} tasks and create running task infos for them
+        final Iterator<TaskRecord> iter = mTmpSortedSet.iterator();
+        while (iter.hasNext()) {
+            if (maxNum == 0) {
+                break;
+            }
+
+            final TaskRecord task = iter.next();
+            list.add(createRunningTaskInfo(task));
+            maxNum--;
+        }
+    }
+
+    /**
+     * Constructs a {@link RunningTaskInfo} from a given {@param task}.
+     */
+    private RunningTaskInfo createRunningTaskInfo(TaskRecord task) {
+        task.getNumRunningActivities(mTmpReport);
+
+        final RunningTaskInfo ci = new RunningTaskInfo();
+        ci.id = task.taskId;
+        ci.stackId = task.getStackId();
+        ci.baseActivity = mTmpReport.base.intent.getComponent();
+        ci.topActivity = mTmpReport.top.intent.getComponent();
+        ci.lastActiveTime = task.lastActiveTime;
+        ci.description = task.lastDescription;
+        ci.numActivities = mTmpReport.numActivities;
+        ci.numRunning = mTmpReport.numRunning;
+        ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
+        ci.resizeMode = task.mResizeMode;
+        ci.configuration.setTo(task.getConfiguration());
+        return ci;
+    }
+}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index f04bdb3..1b5a1ce 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -112,6 +112,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.util.XmlUtils;
+import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.wm.AppWindowContainerController;
 import com.android.server.wm.ConfigurationContainer;
 import com.android.server.wm.StackWindowController;
@@ -1060,6 +1061,36 @@
         return null;
     }
 
+    /**
+     * Return the number of running activities, and the number of non-finishing/initializing
+     * activities in the provided {@param reportOut} respectively.
+     */
+    void getNumRunningActivities(TaskActivitiesReport reportOut) {
+        reportOut.reset();
+        for (int i = mActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.finishing) {
+                continue;
+            }
+
+            reportOut.base = r;
+
+            // Increment the total number of non-finishing activities
+            reportOut.numActivities++;
+
+            if (reportOut.top == null || (reportOut.top.state == ActivityState.INITIALIZING)) {
+                reportOut.top = r;
+                // Reset the number of running activities until we hit the first non-initializing
+                // activity
+                reportOut.numRunning = 0;
+            }
+            if (r.app != null && r.app.thread != null) {
+                // Increment the number of actually running activities
+                reportOut.numRunning++;
+            }
+        }
+    }
+
     boolean okToShowLocked() {
         // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
         // okay to show the activity when locked.
@@ -2205,7 +2236,7 @@
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         proto.write(ID, taskId);
         for (int i = mActivities.size() - 1; i >= 0; i--) {
             ActivityRecord activity = mActivities.get(i);
@@ -2231,4 +2262,19 @@
         proto.write(MIN_HEIGHT, mMinHeight);
         proto.end(token);
     }
+
+    /**
+     * See {@link #getNumRunningActivities(TaskActivitiesReport)}.
+     */
+    static class TaskActivitiesReport {
+        int numRunning;
+        int numActivities;
+        ActivityRecord top;
+        ActivityRecord base;
+
+        void reset() {
+            numRunning = numActivities = 0;
+            top = base = null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 4aa8adb9..1b42818 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -250,21 +250,21 @@
     }
 
     void stopRunningUsersLU(int maxRunningUsers) {
-        int num = mUserLru.size();
+        int currentlyRunning = mUserLru.size();
         int i = 0;
-        while (num > maxRunningUsers && i < mUserLru.size()) {
+        while (currentlyRunning > maxRunningUsers && i < mUserLru.size()) {
             Integer oldUserId = mUserLru.get(i);
             UserState oldUss = mStartedUsers.get(oldUserId);
             if (oldUss == null) {
                 // Shouldn't happen, but be sane if it does.
                 mUserLru.remove(i);
-                num--;
+                currentlyRunning--;
                 continue;
             }
             if (oldUss.state == UserState.STATE_STOPPING
                     || oldUss.state == UserState.STATE_SHUTDOWN) {
                 // This user is already stopping, doesn't count.
-                num--;
+                currentlyRunning--;
                 i++;
                 continue;
             }
@@ -272,16 +272,15 @@
                 // Owner/System user and current user can't be stopped. We count it as running
                 // when it is not a pure system user.
                 if (UserInfo.isSystemOnly(oldUserId)) {
-                    num--;
+                    currentlyRunning--;
                 }
                 i++;
                 continue;
             }
             // This is a user to be stopped.
-            if (stopUsersLU(oldUserId, false, null) != USER_OP_SUCCESS) {
-                num--;
+            if (stopUsersLU(oldUserId, false, null) == USER_OP_SUCCESS) {
+                currentlyRunning--;
             }
-            num--;
             i++;
         }
     }
@@ -2021,7 +2020,7 @@
 
         void loadUserRecents(int userId) {
             synchronized (mService) {
-                mService.mRecentTasks.loadUserRecentsLocked(userId);
+                mService.getRecentTasks().loadUserRecentsLocked(userId);
             }
         }
 
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
new file mode 100644
index 0000000..8981db1
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.net.LinkProperties;
+import android.net.metrics.DefaultNetworkEvent;
+import android.net.metrics.IpConnectivityLog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tracks events related to the default network for the purpose of default network metrics.
+ * {@hide}
+ */
+public class DefaultNetworkMetrics {
+
+    private static final int ROLLING_LOG_SIZE = 64;
+
+    // Event buffer used for metrics upload. The buffer is cleared when events are collected.
+    @GuardedBy("this")
+    private final List<DefaultNetworkEvent> mEvents = new ArrayList<>();
+
+    public synchronized void listEvents(PrintWriter pw) {
+        long localTimeMs = System.currentTimeMillis();
+        for (DefaultNetworkEvent ev : mEvents) {
+            pw.println(ev);
+        }
+    }
+
+    public synchronized void listEventsAsProto(PrintWriter pw) {
+        for (DefaultNetworkEvent ev : mEvents) {
+            pw.print(IpConnectivityEventBuilder.toProto(ev));
+        }
+    }
+
+    public synchronized void flushEvents(List<IpConnectivityEvent> out) {
+        for (DefaultNetworkEvent ev : mEvents) {
+            out.add(IpConnectivityEventBuilder.toProto(ev));
+        }
+        mEvents.clear();
+    }
+
+    public synchronized void logDefaultNetworkEvent(
+            NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
+        DefaultNetworkEvent ev = new DefaultNetworkEvent();
+        if (newNai != null) {
+            ev.netId = newNai.network().netId;
+            ev.transportTypes = newNai.networkCapabilities.getTransportTypes();
+        }
+        if (prevNai != null) {
+            ev.prevNetId = prevNai.network().netId;
+            final LinkProperties lp = prevNai.linkProperties;
+            ev.prevIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute();
+            ev.prevIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
+        }
+
+        mEvents.add(ev);
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index 67e7216..3d71ecb 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -132,6 +132,18 @@
         return out;
     }
 
+    public static IpConnectivityEvent toProto(DefaultNetworkEvent in) {
+        IpConnectivityLogClass.DefaultNetworkEvent ev =
+                new IpConnectivityLogClass.DefaultNetworkEvent();
+        ev.networkId = netIdOf(in.netId);
+        ev.previousNetworkId = netIdOf(in.prevNetId);
+        ev.transportTypes = in.transportTypes;
+        ev.previousNetworkIpSupport = ipSupportOf(in);
+        final IpConnectivityEvent out = buildEvent(in.netId, 0, null);
+        out.setDefaultNetworkEvent(ev);
+        return out;
+    }
+
     private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) {
         final IpConnectivityEvent ev = new IpConnectivityEvent();
         ev.networkId = netId;
@@ -164,11 +176,6 @@
             return true;
         }
 
-        if (in instanceof DefaultNetworkEvent) {
-            setDefaultNetworkEvent(out, (DefaultNetworkEvent) in);
-            return true;
-        }
-
         if (in instanceof NetworkEvent) {
             setNetworkEvent(out, (NetworkEvent) in);
             return true;
@@ -225,16 +232,6 @@
         out.setIpReachabilityEvent(ipReachabilityEvent);
     }
 
-    private static void setDefaultNetworkEvent(IpConnectivityEvent out, DefaultNetworkEvent in) {
-        IpConnectivityLogClass.DefaultNetworkEvent defaultNetworkEvent =
-                new IpConnectivityLogClass.DefaultNetworkEvent();
-        defaultNetworkEvent.networkId = netIdOf(in.netId);
-        defaultNetworkEvent.previousNetworkId = netIdOf(in.prevNetId);
-        defaultNetworkEvent.transportTypes = in.transportTypes;
-        defaultNetworkEvent.previousNetworkIpSupport = ipSupportOf(in);
-        out.setDefaultNetworkEvent(defaultNetworkEvent);
-    }
-
     private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) {
         IpConnectivityLogClass.NetworkEvent networkEvent =
                 new IpConnectivityLogClass.NetworkEvent();
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index f2445fa..24217e6 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -32,12 +32,15 @@
 import android.util.ArrayMap;
 import android.util.Base64;
 import android.util.Log;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.RingBuffer;
 import com.android.internal.util.TokenBucket;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -112,6 +115,9 @@
 
     private final ToIntFunction<Context> mCapacityGetter;
 
+    @VisibleForTesting
+    final DefaultNetworkMetrics mDefaultNetworkMetrics = new DefaultNetworkMetrics();
+
     public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
         super(ctx);
         mCapacityGetter = capacityGetter;
@@ -135,6 +141,8 @@
 
             publishBinderService(SERVICE_NAME, impl);
             publishBinderService(mNetdListener.SERVICE_NAME, mNetdListener);
+
+            LocalServices.addService(Logger.class, new LoggerImpl());
         }
     }
 
@@ -188,6 +196,8 @@
 
         final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events);
 
+        mDefaultNetworkMetrics.flushEvents(protoEvents);
+
         if (mNetdListener != null) {
             mNetdListener.flushStatistics(protoEvents);
         }
@@ -228,6 +238,7 @@
             if (mNetdListener != null) {
                 mNetdListener.listAsProtos(pw);
             }
+            mDefaultNetworkMetrics.listEventsAsProto(pw);
             return;
         }
 
@@ -237,6 +248,7 @@
         if (mNetdListener != null) {
             mNetdListener.list(pw);
         }
+        mDefaultNetworkMetrics.listEvents(pw);
     }
 
     /**
@@ -254,6 +266,7 @@
         if (mNetdListener != null) {
             mNetdListener.list(pw);
         }
+        mDefaultNetworkMetrics.listEvents(pw);
     }
 
     private void cmdStats(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -366,4 +379,15 @@
         map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
         return map;
     }
+
+    /** Direct non-Binder interface for event producer clients within the system servers. */
+    public interface Logger {
+        DefaultNetworkMetrics defaultNetworkMetrics();
+    }
+
+    private class LoggerImpl implements Logger {
+        public DefaultNetworkMetrics defaultNetworkMetrics() {
+            return mDefaultNetworkMetrics;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a4a8e6f..40a02b9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -288,7 +288,21 @@
 
         final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
-            final PackageParser.Package pkg = mPackageManagerInt.getPackage(packages[0]);
+Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages));
+            PackageParser.Package pkg = null;
+            for (String packageName : packages) {
+                pkg = mPackageManagerInt.getPackage(packageName);
+                if (pkg != null) {
+Slog.e(TAG, "TODD: Using packge: " + packageName);
+                    break;
+                } else {
+Slog.e(TAG, "TODD: Missing packge: " + packageName);
+                }
+            }
+            if (pkg == null) {
+Slog.e(TAG, "TODD: No package not found; UID: " + uid);
+                return PackageManager.PERMISSION_DENIED;
+            }
             if (pkg.mSharedUserId != null) {
                 if (isCallerInstantApp) {
                     return PackageManager.PERMISSION_DENIED;
@@ -1058,7 +1072,7 @@
                         // before.  In this case we do want to allow the app to
                         // now get the new permission if the ancestral apk is
                         // privileged to get it.
-                        if (disabledPkg != null
+                        if (disabledPs != null && disabledPkg != null
                                 && isPackageRequestingPermission(disabledPkg, perm)
                                 && ((privilegedPermission && disabledPs.isPrivileged())
                                         || (oemPermission && disabledPs.isOem()
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b917dae..2494bde 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -58,10 +58,6 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.dreams.DreamManagerInternal;
-import android.service.power.PowerServiceDumpProto;
-import android.service.power.PowerServiceSettingsAndConfigurationDumpProto;
-import android.service.power.SuspendBlockerProto;
-import android.service.power.WakeLockProto;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.util.EventLog;
@@ -620,8 +616,8 @@
         }
 
         void dumpProto(ProtoOutputStream proto) {
-            final long constantsToken = proto.start(PowerServiceDumpProto.CONSTANTS);
-            proto.write(PowerServiceDumpProto.ConstantsProto.IS_NO_CACHED_WAKE_LOCKS,
+            final long constantsToken = proto.start(PowerManagerServiceDumpProto.CONSTANTS);
+            proto.write(PowerManagerServiceDumpProto.ConstantsProto.IS_NO_CACHED_WAKE_LOCKS,
                     NO_CACHED_WAKE_LOCKS);
             proto.end(constantsToken);
         }
@@ -3396,112 +3392,112 @@
 
         synchronized (mLock) {
             mConstants.dumpProto(proto);
-            proto.write(PowerServiceDumpProto.DIRTY, mDirty);
-            proto.write(PowerServiceDumpProto.WAKEFULNESS, mWakefulness);
-            proto.write(PowerServiceDumpProto.IS_WAKEFULNESS_CHANGING, mWakefulnessChanging);
-            proto.write(PowerServiceDumpProto.IS_POWERED, mIsPowered);
-            proto.write(PowerServiceDumpProto.PLUG_TYPE, mPlugType);
-            proto.write(PowerServiceDumpProto.BATTERY_LEVEL, mBatteryLevel);
+            proto.write(PowerManagerServiceDumpProto.DIRTY, mDirty);
+            proto.write(PowerManagerServiceDumpProto.WAKEFULNESS, mWakefulness);
+            proto.write(PowerManagerServiceDumpProto.IS_WAKEFULNESS_CHANGING, mWakefulnessChanging);
+            proto.write(PowerManagerServiceDumpProto.IS_POWERED, mIsPowered);
+            proto.write(PowerManagerServiceDumpProto.PLUG_TYPE, mPlugType);
+            proto.write(PowerManagerServiceDumpProto.BATTERY_LEVEL, mBatteryLevel);
             proto.write(
-                    PowerServiceDumpProto.BATTERY_LEVEL_WHEN_DREAM_STARTED,
+                    PowerManagerServiceDumpProto.BATTERY_LEVEL_WHEN_DREAM_STARTED,
                     mBatteryLevelWhenDreamStarted);
-            proto.write(PowerServiceDumpProto.DOCK_STATE, mDockState);
-            proto.write(PowerServiceDumpProto.IS_STAY_ON, mStayOn);
-            proto.write(PowerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive);
-            proto.write(PowerServiceDumpProto.IS_BOOT_COMPLETED, mBootCompleted);
-            proto.write(PowerServiceDumpProto.IS_SYSTEM_READY, mSystemReady);
+            proto.write(PowerManagerServiceDumpProto.DOCK_STATE, mDockState);
+            proto.write(PowerManagerServiceDumpProto.IS_STAY_ON, mStayOn);
+            proto.write(PowerManagerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive);
+            proto.write(PowerManagerServiceDumpProto.IS_BOOT_COMPLETED, mBootCompleted);
+            proto.write(PowerManagerServiceDumpProto.IS_SYSTEM_READY, mSystemReady);
             proto.write(
-                    PowerServiceDumpProto.IS_HAL_AUTO_SUSPEND_MODE_ENABLED,
+                    PowerManagerServiceDumpProto.IS_HAL_AUTO_SUSPEND_MODE_ENABLED,
                     mHalAutoSuspendModeEnabled);
             proto.write(
-                    PowerServiceDumpProto.IS_HAL_AUTO_INTERACTIVE_MODE_ENABLED,
+                    PowerManagerServiceDumpProto.IS_HAL_AUTO_INTERACTIVE_MODE_ENABLED,
                     mHalInteractiveModeEnabled);
 
-            final long activeWakeLocksToken = proto.start(PowerServiceDumpProto.ACTIVE_WAKE_LOCKS);
+            final long activeWakeLocksToken = proto.start(PowerManagerServiceDumpProto.ACTIVE_WAKE_LOCKS);
             proto.write(
-                    PowerServiceDumpProto.ActiveWakeLocksProto.IS_CPU,
+                    PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_CPU,
                     (mWakeLockSummary & WAKE_LOCK_CPU) != 0);
             proto.write(
-                    PowerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_BRIGHT,
+                    PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_BRIGHT,
                     (mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0);
             proto.write(
-                    PowerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_DIM,
+                    PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_DIM,
                     (mWakeLockSummary & WAKE_LOCK_SCREEN_DIM) != 0);
             proto.write(
-                    PowerServiceDumpProto.ActiveWakeLocksProto.IS_BUTTON_BRIGHT,
+                    PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_BUTTON_BRIGHT,
                     (mWakeLockSummary & WAKE_LOCK_BUTTON_BRIGHT) != 0);
             proto.write(
-                    PowerServiceDumpProto.ActiveWakeLocksProto.IS_PROXIMITY_SCREEN_OFF,
+                    PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_PROXIMITY_SCREEN_OFF,
                     (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0);
             proto.write(
-                    PowerServiceDumpProto.ActiveWakeLocksProto.IS_STAY_AWAKE,
+                    PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_STAY_AWAKE,
                     (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0);
             proto.write(
-                    PowerServiceDumpProto.ActiveWakeLocksProto.IS_DOZE,
+                    PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_DOZE,
                     (mWakeLockSummary & WAKE_LOCK_DOZE) != 0);
             proto.write(
-                    PowerServiceDumpProto.ActiveWakeLocksProto.IS_DRAW,
+                    PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_DRAW,
                     (mWakeLockSummary & WAKE_LOCK_DRAW) != 0);
             proto.end(activeWakeLocksToken);
 
-            proto.write(PowerServiceDumpProto.NOTIFY_LONG_SCHEDULED_MS, mNotifyLongScheduled);
-            proto.write(PowerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
-            proto.write(PowerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
+            proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_SCHEDULED_MS, mNotifyLongScheduled);
+            proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
+            proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
 
-            final long userActivityToken = proto.start(PowerServiceDumpProto.USER_ACTIVITY);
+            final long userActivityToken = proto.start(PowerManagerServiceDumpProto.USER_ACTIVITY);
             proto.write(
-                    PowerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
+                    PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
                     (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
             proto.write(
-                    PowerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
+                    PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
                     (mUserActivitySummary & USER_ACTIVITY_SCREEN_DIM) != 0);
             proto.write(
-                    PowerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
+                    PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
                     (mUserActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
             proto.end(userActivityToken);
 
             proto.write(
-                    PowerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
+                    PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
                     mRequestWaitForNegativeProximity);
-            proto.write(PowerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled);
-            proto.write(PowerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned);
-            proto.write(PowerServiceDumpProto.IS_LOW_POWER_MODE_ENABLED, mLowPowerModeEnabled);
-            proto.write(PowerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow);
-            proto.write(PowerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode);
-            proto.write(PowerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode);
+            proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled);
+            proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned);
+            proto.write(PowerManagerServiceDumpProto.IS_LOW_POWER_MODE_ENABLED, mLowPowerModeEnabled);
+            proto.write(PowerManagerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow);
+            proto.write(PowerManagerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode);
+            proto.write(PowerManagerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode);
 
             for (int id : mDeviceIdleWhitelist) {
-                proto.write(PowerServiceDumpProto.DEVICE_IDLE_WHITELIST, id);
+                proto.write(PowerManagerServiceDumpProto.DEVICE_IDLE_WHITELIST, id);
             }
             for (int id : mDeviceIdleTempWhitelist) {
-                proto.write(PowerServiceDumpProto.DEVICE_IDLE_TEMP_WHITELIST, id);
+                proto.write(PowerManagerServiceDumpProto.DEVICE_IDLE_TEMP_WHITELIST, id);
             }
 
-            proto.write(PowerServiceDumpProto.LAST_WAKE_TIME_MS, mLastWakeTime);
-            proto.write(PowerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastSleepTime);
-            proto.write(PowerServiceDumpProto.LAST_USER_ACTIVITY_TIME_MS, mLastUserActivityTime);
+            proto.write(PowerManagerServiceDumpProto.LAST_WAKE_TIME_MS, mLastWakeTime);
+            proto.write(PowerManagerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastSleepTime);
+            proto.write(PowerManagerServiceDumpProto.LAST_USER_ACTIVITY_TIME_MS, mLastUserActivityTime);
             proto.write(
-                    PowerServiceDumpProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
+                    PowerManagerServiceDumpProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
                     mLastUserActivityTimeNoChangeLights);
             proto.write(
-                    PowerServiceDumpProto.LAST_INTERACTIVE_POWER_HINT_TIME_MS,
+                    PowerManagerServiceDumpProto.LAST_INTERACTIVE_POWER_HINT_TIME_MS,
                     mLastInteractivePowerHintTime);
             proto.write(
-                    PowerServiceDumpProto.LAST_SCREEN_BRIGHTNESS_BOOST_TIME_MS,
+                    PowerManagerServiceDumpProto.LAST_SCREEN_BRIGHTNESS_BOOST_TIME_MS,
                     mLastScreenBrightnessBoostTime);
             proto.write(
-                    PowerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS,
+                    PowerManagerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS,
                     mScreenBrightnessBoostInProgress);
-            proto.write(PowerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady);
+            proto.write(PowerManagerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady);
             proto.write(
-                    PowerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER,
+                    PowerManagerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER,
                     mHoldingWakeLockSuspendBlocker);
             proto.write(
-                    PowerServiceDumpProto.IS_HOLDING_DISPLAY_SUSPEND_BLOCKER,
+                    PowerManagerServiceDumpProto.IS_HOLDING_DISPLAY_SUSPEND_BLOCKER,
                     mHoldingDisplaySuspendBlocker);
 
             final long settingsAndConfigurationToken =
-                    proto.start(PowerServiceDumpProto.SETTINGS_AND_CONFIGURATION);
+                    proto.start(PowerManagerServiceDumpProto.SETTINGS_AND_CONFIGURATION);
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto
                             .IS_DECOUPLE_HAL_AUTO_SUSPEND_MODE_FROM_DISPLAY_CONFIG,
@@ -3698,42 +3694,43 @@
             final int sleepTimeout = getSleepTimeoutLocked();
             final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
             final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
-            proto.write(PowerServiceDumpProto.SLEEP_TIMEOUT_MS, sleepTimeout);
-            proto.write(PowerServiceDumpProto.SCREEN_OFF_TIMEOUT_MS, screenOffTimeout);
-            proto.write(PowerServiceDumpProto.SCREEN_DIM_DURATION_MS, screenDimDuration);
-            proto.write(PowerServiceDumpProto.ARE_UIDS_CHANGING, mUidsChanging);
-            proto.write(PowerServiceDumpProto.ARE_UIDS_CHANGED, mUidsChanged);
+            proto.write(PowerManagerServiceDumpProto.SLEEP_TIMEOUT_MS, sleepTimeout);
+            proto.write(PowerManagerServiceDumpProto.SCREEN_OFF_TIMEOUT_MS, screenOffTimeout);
+            proto.write(PowerManagerServiceDumpProto.SCREEN_DIM_DURATION_MS, screenDimDuration);
+            proto.write(PowerManagerServiceDumpProto.ARE_UIDS_CHANGING, mUidsChanging);
+            proto.write(PowerManagerServiceDumpProto.ARE_UIDS_CHANGED, mUidsChanged);
 
             for (int i = 0; i < mUidState.size(); i++) {
                 final UidState state = mUidState.valueAt(i);
-                final long uIDToken = proto.start(PowerServiceDumpProto.UIDS);
+                final long uIDToken = proto.start(PowerManagerServiceDumpProto.UID_STATES);
                 final int uid = mUidState.keyAt(i);
-                proto.write(PowerServiceDumpProto.UidProto.UID, uid);
-                proto.write(PowerServiceDumpProto.UidProto.UID_STRING, UserHandle.formatUid(uid));
-                proto.write(PowerServiceDumpProto.UidProto.IS_ACTIVE, state.mActive);
-                proto.write(PowerServiceDumpProto.UidProto.NUM_WAKE_LOCKS, state.mNumWakeLocks);
+                proto.write(PowerManagerServiceDumpProto.UidStateProto.UID, uid);
+                proto.write(PowerManagerServiceDumpProto.UidStateProto.UID_STRING, UserHandle.formatUid(uid));
+                proto.write(PowerManagerServiceDumpProto.UidStateProto.IS_ACTIVE, state.mActive);
+                proto.write(PowerManagerServiceDumpProto.UidStateProto.NUM_WAKE_LOCKS, state.mNumWakeLocks);
                 if (state.mProcState == ActivityManager.PROCESS_STATE_UNKNOWN) {
-                    proto.write(PowerServiceDumpProto.UidProto.IS_PROCESS_STATE_UNKNOWN, true);
+                    proto.write(PowerManagerServiceDumpProto.UidStateProto.IS_PROCESS_STATE_UNKNOWN, true);
                 } else {
-                    proto.write(PowerServiceDumpProto.UidProto.PROCESS_STATE, state.mProcState);
+                    proto.write(PowerManagerServiceDumpProto.UidStateProto.PROCESS_STATE,
+                            ActivityManager.processStateAmToProto(state.mProcState));
                 }
                 proto.end(uIDToken);
             }
 
-            mHandler.getLooper().writeToProto(proto, PowerServiceDumpProto.LOOPER);
+            mHandler.getLooper().writeToProto(proto, PowerManagerServiceDumpProto.LOOPER);
 
             for (WakeLock wl : mWakeLocks) {
-                wl.writeToProto(proto, PowerServiceDumpProto.WAKE_LOCKS);
+                wl.writeToProto(proto, PowerManagerServiceDumpProto.WAKE_LOCKS);
             }
 
             for (SuspendBlocker sb : mSuspendBlockers) {
-                sb.writeToProto(proto, PowerServiceDumpProto.SUSPEND_BLOCKERS);
+                sb.writeToProto(proto, PowerManagerServiceDumpProto.SUSPEND_BLOCKERS);
             }
             wcd = mWirelessChargerDetector;
         }
 
         if (wcd != null) {
-            wcd.writeToProto(proto, PowerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
+            wcd.writeToProto(proto, PowerManagerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
         }
         proto.flush();
     }
diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java
index 6ee9dcd..54487e3 100644
--- a/services/core/java/com/android/server/power/WirelessChargerDetector.java
+++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java
@@ -24,7 +24,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
-import android.service.power.WirelessChargerDetectorProto;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 5d03493..e873d32 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1621,10 +1621,10 @@
 
     @CallSuper
     @Override
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
         final long token = proto.start(fieldId);
         writeNameToProto(proto, NAME);
-        super.writeToProto(proto, WINDOW_TOKEN);
+        super.writeToProto(proto, WINDOW_TOKEN, trim);
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index a4ab3ba..cc94807 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -50,6 +50,9 @@
     /** Contains override configuration settings applied to this configuration container. */
     private Configuration mOverrideConfiguration = new Configuration();
 
+    /** True if mOverrideConfiguration is not empty */
+    private boolean mHasOverrideConfiguration;
+
     /**
      * Contains full configuration applied to this configuration container. Corresponds to full
      * parent's config with applied {@link #mOverrideConfiguration}.
@@ -101,6 +104,9 @@
      * @see #mFullConfiguration
      */
     public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        // Pre-compute this here, so we don't need to go through the entire Configuration when
+        // writing to proto (which has significant cost if we write a lot of empty configurations).
+        mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
         mOverrideConfiguration.setTo(overrideConfiguration);
         // Update full configuration of this container and all its children.
         final ConfigurationContainer parent = getParent();
@@ -330,18 +336,23 @@
      * Write to a protocol buffer output stream. Protocol buffer message definition is at
      * {@link com.android.server.wm.proto.ConfigurationContainerProto}.
      *
-     * @param protoOutputStream Stream to write the ConfigurationContainer object to.
-     * @param fieldId           Field Id of the ConfigurationContainer as defined in the parent
-     *                          message.
+     * @param proto    Stream to write the ConfigurationContainer object to.
+     * @param fieldId  Field Id of the ConfigurationContainer as defined in the parent
+     *                 message.
+     * @param trim     If true, reduce amount of data written.
      * @hide
      */
     @CallSuper
-    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
-        final long token = protoOutputStream.start(fieldId);
-        mOverrideConfiguration.writeToProto(protoOutputStream, OVERRIDE_CONFIGURATION);
-        mFullConfiguration.writeToProto(protoOutputStream, FULL_CONFIGURATION);
-        mMergedOverrideConfiguration.writeToProto(protoOutputStream, MERGED_OVERRIDE_CONFIGURATION);
-        protoOutputStream.end(token);
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
+        final long token = proto.start(fieldId);
+        if (!trim || mHasOverrideConfiguration) {
+            mOverrideConfiguration.writeToProto(proto, OVERRIDE_CONFIGURATION);
+        }
+        if (!trim) {
+            mFullConfiguration.writeToProto(proto, FULL_CONFIGURATION);
+            mMergedOverrideConfiguration.writeToProto(proto, MERGED_OVERRIDE_CONFIGURATION);
+        }
+        proto.end(token);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 03fdc96..4c6ab3f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2137,27 +2137,27 @@
 
     @CallSuper
     @Override
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, WINDOW_CONTAINER);
+        super.writeToProto(proto, WINDOW_CONTAINER, trim);
         proto.write(ID, mDisplayId);
         for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
-            stack.writeToProto(proto, STACKS);
+            stack.writeToProto(proto, STACKS, trim);
         }
         mDividerControllerLocked.writeToProto(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
         mPinnedStackControllerLocked.writeToProto(proto, PINNED_STACK_CONTROLLER);
         for (int i = mAboveAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
             final WindowToken windowToken = mAboveAppWindowsContainers.getChildAt(i);
-            windowToken.writeToProto(proto, ABOVE_APP_WINDOWS);
+            windowToken.writeToProto(proto, ABOVE_APP_WINDOWS, trim);
         }
         for (int i = mBelowAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
             final WindowToken windowToken = mBelowAppWindowsContainers.getChildAt(i);
-            windowToken.writeToProto(proto, BELOW_APP_WINDOWS);
+            windowToken.writeToProto(proto, BELOW_APP_WINDOWS, trim);
         }
         for (int i = mImeWindowsContainers.getChildCount() - 1; i >= 0; --i) {
             final WindowToken windowToken = mImeWindowsContainers.getChildAt(i);
-            windowToken.writeToProto(proto, IME_WINDOWS);
+            windowToken.writeToProto(proto, IME_WINDOWS, trim);
         }
         proto.write(DPI, mBaseDisplayDensity);
         mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
new file mode 100644
index 0000000..36871b1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.graphics.PixelFormat.TRANSLUCENT;
+import static android.view.SurfaceControl.HIDDEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.ClipData;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Slog;
+import android.view.Display;
+import android.view.IWindow;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.view.View;
+import com.android.server.wm.WindowManagerService.H;
+
+/**
+ * Managing drag and drop operations initiated by View#startDragAndDrop.
+ */
+class DragDropController {
+    private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
+    private static final long DRAG_TIMEOUT_MS = 5000;
+
+    IBinder prepareDrag(WindowManagerService service, SurfaceSession session, int callerPid,
+            int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
+        if (DEBUG_DRAG) {
+            Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
+                    + " flags=" + Integer.toHexString(flags) + " win=" + window
+                    + " asbinder=" + window.asBinder());
+        }
+
+        IBinder token = null;
+
+        synchronized (service.mWindowMap) {
+            try {
+                if (service.mDragState == null) {
+                    // TODO(multi-display): support other displays
+                    final DisplayContent displayContent =
+                            service.getDefaultDisplayContentLocked();
+                    final Display display = displayContent.getDisplay();
+
+                    SurfaceControl surface = new SurfaceControl(session, "drag surface",
+                            width, height, TRANSLUCENT, HIDDEN);
+                    surface.setLayerStack(display.getLayerStack());
+                    float alpha = 1;
+                    if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
+                        alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
+                    }
+                    surface.setAlpha(alpha);
+
+                    if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  DRAG "
+                            + surface + ": CREATE");
+                    outSurface.copyFrom(surface);
+                    final IBinder winBinder = window.asBinder();
+                    token = new Binder();
+                    service.mDragState =
+                            new DragState(service, token, surface, flags, winBinder);
+                    service.mDragState.mPid = callerPid;
+                    service.mDragState.mUid = callerUid;
+                    service.mDragState.mOriginalAlpha = alpha;
+                    token = service.mDragState.mToken = new Binder();
+
+                    // 5 second timeout for this window to actually begin the drag
+                    service.mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
+                    Message msg = service.mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
+                    service.mH.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
+                } else {
+                    Slog.w(TAG_WM, "Drag already in progress");
+                }
+            } catch (OutOfResourcesException e) {
+                Slog.e(TAG_WM, "Can't allocate drag surface w=" + width + " h=" + height,
+                        e);
+                if (service.mDragState != null) {
+                    service.mDragState.reset();
+                    service.mDragState = null;
+                }
+            }
+        }
+
+        return token;
+    }
+
+    boolean performDrag(WindowManagerService service, IWindow window, IBinder dragToken,
+            int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+            ClipData data) {
+        if (DEBUG_DRAG) {
+            Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
+        }
+
+        synchronized (service.mWindowMap) {
+            if (service.mDragState == null) {
+                Slog.w(TAG_WM, "No drag prepared");
+                throw new IllegalStateException("performDrag() without prepareDrag()");
+            }
+
+            if (dragToken != service.mDragState.mToken) {
+                Slog.w(TAG_WM, "Performing mismatched drag");
+                throw new IllegalStateException("performDrag() does not match prepareDrag()");
+            }
+
+            final WindowState callingWin = service.windowForClientLocked(null, window, false);
+            if (callingWin == null) {
+                Slog.w(TAG_WM, "Bad requesting window " + window);
+                return false;  // !!! TODO: throw here?
+            }
+
+            // !!! TODO: if input is not still focused on the initiating window, fail
+            // the drag initiation (e.g. an alarm window popped up just as the application
+            // called performDrag()
+
+            service.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
+
+            // !!! TODO: extract the current touch (x, y) in screen coordinates.  That
+            // will let us eliminate the (touchX,touchY) parameters from the API.
+
+            // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
+            // the actual drag event dispatch stuff in the dragstate
+
+            final DisplayContent displayContent = callingWin.getDisplayContent();
+            if (displayContent == null) {
+                return false;
+            }
+            Display display = displayContent.getDisplay();
+            service.mDragState.register(display);
+            if (!service.mInputManager.transferTouchFocus(callingWin.mInputChannel,
+                    service.mDragState.getInputChannel())) {
+                Slog.e(TAG_WM, "Unable to transfer touch focus");
+                service.mDragState.unregister();
+                service.mDragState.reset();
+                service.mDragState = null;
+                return false;
+            }
+
+            service.mDragState.mDisplayContent = displayContent;
+            service.mDragState.mData = data;
+            service.mDragState.broadcastDragStartedLw(touchX, touchY);
+            service.mDragState.overridePointerIconLw(touchSource);
+
+            // remember the thumb offsets for later
+            service.mDragState.mThumbOffsetX = thumbCenterX;
+            service.mDragState.mThumbOffsetY = thumbCenterY;
+
+            // Make the surface visible at the proper location
+            final SurfaceControl surfaceControl = service.mDragState.mSurfaceControl;
+            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
+                    TAG_WM, ">>> OPEN TRANSACTION performDrag");
+            service.openSurfaceTransaction();
+            try {
+                surfaceControl.setPosition(touchX - thumbCenterX,
+                        touchY - thumbCenterY);
+                surfaceControl.setLayer(service.mDragState.getDragLayerLw());
+                surfaceControl.setLayerStack(display.getLayerStack());
+                surfaceControl.show();
+            } finally {
+                service.closeSurfaceTransaction();
+                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
+                        TAG_WM, "<<< CLOSE TRANSACTION performDrag");
+            }
+
+            service.mDragState.notifyLocationLw(touchX, touchY);
+        }
+
+        return true;    // success!
+    }
+
+    void reportDropResult(WindowManagerService service, IWindow window, boolean consumed) {
+        IBinder token = window.asBinder();
+        if (DEBUG_DRAG) {
+            Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
+        }
+
+        synchronized (service.mWindowMap) {
+            if (service.mDragState == null) {
+                // Most likely the drop recipient ANRed and we ended the drag
+                // out from under it.  Log the issue and move on.
+                Slog.w(TAG_WM, "Drop result given but no drag in progress");
+                return;
+            }
+
+            if (service.mDragState.mToken != token) {
+                // We're in a drag, but the wrong window has responded.
+                Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
+                throw new IllegalStateException("reportDropResult() by non-recipient");
+            }
+
+            // The right window has responded, even if it's no longer around,
+            // so be sure to halt the timeout even if the later WindowState
+            // lookup fails.
+            service.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
+            WindowState callingWin = service.windowForClientLocked(null, window, false);
+            if (callingWin == null) {
+                Slog.w(TAG_WM, "Bad result-reporting window " + window);
+                return;  // !!! TODO: throw here?
+            }
+
+            service.mDragState.mDragResult = consumed;
+            service.mDragState.endDragLw();
+        }
+    }
+
+    void cancelDragAndDrop(WindowManagerService service, IBinder dragToken) {
+        if (DEBUG_DRAG) {
+            Slog.d(TAG_WM, "cancelDragAndDrop");
+        }
+
+        synchronized (service.mWindowMap) {
+            if (service.mDragState == null) {
+                Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
+                throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
+            }
+
+            if (service.mDragState.mToken != dragToken) {
+                Slog.w(TAG_WM,
+                        "cancelDragAndDrop() does not match prepareDrag()");
+                throw new IllegalStateException(
+                        "cancelDragAndDrop() does not match prepareDrag()");
+            }
+
+            service.mDragState.mDragResult = false;
+            service.mDragState.cancelDragLw();
+        }
+    }
+
+    void dragRecipientEntered(IWindow window) {
+        if (DEBUG_DRAG) {
+            Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
+        }
+    }
+
+    void dragRecipientExited(IWindow window) {
+        if (DEBUG_DRAG) {
+            Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
+        }
+    }
+
+    void handleMessage(WindowManagerService service, Message msg) {
+        switch (msg.what) {
+            case H.DRAG_START_TIMEOUT: {
+                IBinder win = (IBinder) msg.obj;
+                if (DEBUG_DRAG) {
+                    Slog.w(TAG_WM, "Timeout starting drag by win " + win);
+                }
+                synchronized (service.mWindowMap) {
+                    // !!! TODO: ANR the app that has failed to start the drag in time
+                    if (service.mDragState != null) {
+                        service.mDragState.unregister();
+                        service.mDragState.reset();
+                        service.mDragState = null;
+                    }
+                }
+                break;
+            }
+
+            case H.DRAG_END_TIMEOUT: {
+                IBinder win = (IBinder) msg.obj;
+                if (DEBUG_DRAG) {
+                    Slog.w(TAG_WM, "Timeout ending drag to win " + win);
+                }
+                synchronized (service.mWindowMap) {
+                    // !!! TODO: ANR the drag-receiving app
+                    if (service.mDragState != null) {
+                        service.mDragState.mDragResult = false;
+                        service.mDragState.endDragLw();
+                    }
+                }
+                break;
+            }
+
+            case H.TEAR_DOWN_DRAG_AND_DROP_INPUT: {
+                if (DEBUG_DRAG)
+                    Slog.d(TAG_WM, "Drag ending; tearing down input channel");
+                DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
+                if (interceptor != null) {
+                    synchronized (service.mWindowMap) {
+                        interceptor.tearDown();
+                    }
+                }
+                break;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index fd57470..a710441 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1081,19 +1081,21 @@
 
     @CallSuper
     @Override
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, WINDOW_CONTAINER);
+        super.writeToProto(proto, WINDOW_CONTAINER, trim);
         if (mService.mDisplayReady) {
             final int count = mChildren.size();
             for (int i = 0; i < count; ++i) {
                 final DisplayContent displayContent = mChildren.get(i);
-                displayContent.writeToProto(proto, DISPLAYS);
+                displayContent.writeToProto(proto, DISPLAYS, trim);
             }
         }
-        forAllWindows((w) -> {
-            w.writeIdentifierToProto(proto, WINDOWS);
-        }, true);
+        if (!trim) {
+            forAllWindows((w) -> {
+                w.writeIdentifierToProto(proto, WINDOWS);
+            }, true);
+        }
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 2bb0235..717c577 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -81,6 +81,7 @@
     private final Set<WindowSurfaceController> mAppOverlaySurfaces = new HashSet<>();
     // Set of visible alert window surfaces connected to this session.
     private final Set<WindowSurfaceController> mAlertWindowSurfaces = new HashSet<>();
+    private final DragDropController mDragDropController;
     final boolean mCanAddInternalSystemWindow;
     final boolean mCanHideNonSystemOverlayWindows;
     final boolean mCanAcquireSleepToken;
@@ -106,6 +107,7 @@
         mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
                 == PERMISSION_GRANTED;
         mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
+        mDragDropController = mService.mDragDropController;
         StringBuilder sb = new StringBuilder();
         sb.append("Session{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
@@ -304,94 +306,56 @@
 
     /* Drag/drop */
     @Override
-    public IBinder prepareDrag(IWindow window, int flags,
-            int width, int height, Surface outSurface) {
-        return mService.prepareDragSurface(window, mSurfaceSession, flags,
-                width, height, outSurface);
+    public IBinder prepareDrag(IWindow window, int flags, int width, int height,
+            Surface outSurface) {
+        final int callerPid = Binder.getCallingPid();
+        final int callerUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mDragDropController.prepareDrag(
+                    mService, mSurfaceSession, callerPid, callerUid, window, flags, width, height,
+                    outSurface);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     @Override
     public boolean performDrag(IWindow window, IBinder dragToken,
             int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
             ClipData data) {
-        if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
+        return mDragDropController.performDrag(mService, window, dragToken, touchSource,
+                touchX, touchY, thumbCenterX, thumbCenterY, data);
+    }
+
+    @Override
+    public void reportDropResult(IWindow window, boolean consumed) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mDragDropController.reportDropResult(mService, window, consumed);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
+    }
 
-        synchronized (mService.mWindowMap) {
-            if (mService.mDragState == null) {
-                Slog.w(TAG_WM, "No drag prepared");
-                throw new IllegalStateException("performDrag() without prepareDrag()");
-            }
-
-            if (dragToken != mService.mDragState.mToken) {
-                Slog.w(TAG_WM, "Performing mismatched drag");
-                throw new IllegalStateException("performDrag() does not match prepareDrag()");
-            }
-
-            WindowState callingWin = mService.windowForClientLocked(null, window, false);
-            if (callingWin == null) {
-                Slog.w(TAG_WM, "Bad requesting window " + window);
-                return false;  // !!! TODO: throw here?
-            }
-
-            // !!! TODO: if input is not still focused on the initiating window, fail
-            // the drag initiation (e.g. an alarm window popped up just as the application
-            // called performDrag()
-
-            mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
-
-            // !!! TODO: extract the current touch (x, y) in screen coordinates.  That
-            // will let us eliminate the (touchX,touchY) parameters from the API.
-
-            // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
-            // the actual drag event dispatch stuff in the dragstate
-
-            final DisplayContent displayContent = callingWin.getDisplayContent();
-            if (displayContent == null) {
-               return false;
-            }
-            Display display = displayContent.getDisplay();
-            mService.mDragState.register(display);
-            if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
-                    mService.mDragState.getInputChannel())) {
-                Slog.e(TAG_WM, "Unable to transfer touch focus");
-                mService.mDragState.unregister();
-                mService.mDragState.reset();
-                mService.mDragState = null;
-                return false;
-            }
-
-            mService.mDragState.mDisplayContent = displayContent;
-            mService.mDragState.mData = data;
-            mService.mDragState.broadcastDragStartedLw(touchX, touchY);
-            mService.mDragState.overridePointerIconLw(touchSource);
-
-            // remember the thumb offsets for later
-            mService.mDragState.mThumbOffsetX = thumbCenterX;
-            mService.mDragState.mThumbOffsetY = thumbCenterY;
-
-            // Make the surface visible at the proper location
-            final SurfaceControl surfaceControl = mService.mDragState.mSurfaceControl;
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
-                    TAG_WM, ">>> OPEN TRANSACTION performDrag");
-            mService.openSurfaceTransaction();
-            try {
-                surfaceControl.setPosition(touchX - thumbCenterX,
-                        touchY - thumbCenterY);
-                surfaceControl.setLayer(mService.mDragState.getDragLayerLw());
-                surfaceControl.setLayerStack(display.getLayerStack());
-                surfaceControl.show();
-            } finally {
-                mService.closeSurfaceTransaction();
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
-                        TAG_WM, "<<< CLOSE TRANSACTION performDrag");
-            }
-
-            mService.mDragState.notifyLocationLw(touchX, touchY);
+    @Override
+    public void cancelDragAndDrop(IBinder dragToken) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mDragDropController.cancelDragAndDrop(mService, dragToken);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
+    }
 
-        return true;    // success!
+    @Override
+    public void dragRecipientEntered(IWindow window) {
+        mDragDropController.dragRecipientEntered(window);
+    }
+
+    @Override
+    public void dragRecipientExited(IWindow window) {
+        mDragDropController.dragRecipientExited(window);
     }
 
     @Override
@@ -408,90 +372,6 @@
     }
 
     @Override
-    public void reportDropResult(IWindow window, boolean consumed) {
-        IBinder token = window.asBinder();
-        if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
-        }
-
-        synchronized (mService.mWindowMap) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                if (mService.mDragState == null) {
-                    // Most likely the drop recipient ANRed and we ended the drag
-                    // out from under it.  Log the issue and move on.
-                    Slog.w(TAG_WM, "Drop result given but no drag in progress");
-                    return;
-                }
-
-                if (mService.mDragState.mToken != token) {
-                    // We're in a drag, but the wrong window has responded.
-                    Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
-                    throw new IllegalStateException("reportDropResult() by non-recipient");
-                }
-
-                // The right window has responded, even if it's no longer around,
-                // so be sure to halt the timeout even if the later WindowState
-                // lookup fails.
-                mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
-                WindowState callingWin = mService.windowForClientLocked(null, window, false);
-                if (callingWin == null) {
-                    Slog.w(TAG_WM, "Bad result-reporting window " + window);
-                    return;  // !!! TODO: throw here?
-                }
-
-                mService.mDragState.mDragResult = consumed;
-                mService.mDragState.endDragLw();
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void cancelDragAndDrop(IBinder dragToken) {
-        if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "cancelDragAndDrop");
-        }
-
-        synchronized (mService.mWindowMap) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                if (mService.mDragState == null) {
-                    Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
-                    throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
-                }
-
-                if (mService.mDragState.mToken != dragToken) {
-                    Slog.w(TAG_WM,
-                            "cancelDragAndDrop() does not match prepareDrag()");
-                    throw new IllegalStateException(
-                            "cancelDragAndDrop() does not match prepareDrag()");
-                }
-
-                mService.mDragState.mDragResult = false;
-                mService.mDragState.cancelDragLw();
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void dragRecipientEntered(IWindow window) {
-        if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
-        }
-    }
-
-    @Override
-    public void dragRecipientExited(IWindow window) {
-        if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
-        }
-    }
-
-    @Override
     public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
         synchronized(mService.mWindowMap) {
             long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 891d637a..7620cb0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -725,13 +725,13 @@
 
     @CallSuper
     @Override
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, WINDOW_CONTAINER);
+        super.writeToProto(proto, WINDOW_CONTAINER, trim);
         proto.write(ID, mTaskId);
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final AppWindowToken appWindowToken = mChildren.get(i);
-            appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS);
+            appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS, trim);
         }
         proto.write(FILLS_PARENT, mFillsParent);
         mBounds.writeToProto(proto, BOUNDS);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 3320274..791accf 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1232,12 +1232,12 @@
 
     @CallSuper
     @Override
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, WINDOW_CONTAINER);
+        super.writeToProto(proto, WINDOW_CONTAINER, trim);
         proto.write(ID, mStackId);
         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
-            mChildren.get(taskNdx).writeToProto(proto, TASKS);
+            mChildren.get(taskNdx).writeToProto(proto, TASKS, trim);
         }
         proto.write(FILLS_PARENT, mFillsParent);
         mBounds.writeToProto(proto, BOUNDS);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 563eb9c..8f4b897 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -677,17 +677,18 @@
      * Write to a protocol buffer output stream. Protocol buffer message definition is at
      * {@link com.android.server.wm.proto.WindowContainerProto}.
      *
-     * @param protoOutputStream Stream to write the WindowContainer object to.
-     * @param fieldId           Field Id of the WindowContainer as defined in the parent message.
+     * @param proto     Stream to write the WindowContainer object to.
+     * @param fieldId   Field Id of the WindowContainer as defined in the parent message.
+     * @param trim      If true, reduce the amount of data written.
      * @hide
      */
     @CallSuper
     @Override
-    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
-        final long token = protoOutputStream.start(fieldId);
-        super.writeToProto(protoOutputStream, CONFIGURATION_CONTAINER);
-        protoOutputStream.write(ORIENTATION, mOrientation);
-        protoOutputStream.end(token);
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, trim);
+        proto.write(ORIENTATION, mOrientation);
+        proto.end(token);
     }
 
     private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5992a5f..ad6fc39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -357,8 +357,6 @@
     // trying to apply a new one.
     private static final boolean ALWAYS_KEEP_CURRENT = true;
 
-    private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
-
     // Enums for animation scale update types.
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
@@ -752,6 +750,7 @@
     boolean mAllowTheaterModeWakeFromLayout;
 
     TaskPositioner mTaskPositioner;
+    final DragDropController mDragDropController = new DragDropController();
     DragState mDragState = null;
 
     // For frozen screen animations.
@@ -4637,73 +4636,6 @@
     }
 
     // -------------------------------------------------------------
-    // Drag and drop
-    // -------------------------------------------------------------
-
-    IBinder prepareDragSurface(IWindow window, SurfaceSession session,
-            int flags, int width, int height, Surface outSurface) {
-        if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
-                    + " flags=" + Integer.toHexString(flags) + " win=" + window
-                    + " asbinder=" + window.asBinder());
-        }
-
-        final int callerPid = Binder.getCallingPid();
-        final int callerUid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        IBinder token = null;
-
-        try {
-            synchronized (mWindowMap) {
-                try {
-                    if (mDragState == null) {
-                        // TODO(multi-display): support other displays
-                        final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                        final Display display = displayContent.getDisplay();
-
-                        SurfaceControl surface = new SurfaceControl(session, "drag surface",
-                                width, height, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
-                        surface.setLayerStack(display.getLayerStack());
-                        float alpha = 1;
-                        if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
-                            alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
-                        }
-                        surface.setAlpha(alpha);
-
-                        if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  DRAG "
-                                + surface + ": CREATE");
-                        outSurface.copyFrom(surface);
-                        final IBinder winBinder = window.asBinder();
-                        token = new Binder();
-                        mDragState = new DragState(this, token, surface, flags, winBinder);
-                        mDragState.mPid = callerPid;
-                        mDragState.mUid = callerUid;
-                        mDragState.mOriginalAlpha = alpha;
-                        token = mDragState.mToken = new Binder();
-
-                        // 5 second timeout for this window to actually begin the drag
-                        mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
-                        Message msg = mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
-                        mH.sendMessageDelayed(msg, 5000);
-                    } else {
-                        Slog.w(TAG_WM, "Drag already in progress");
-                    }
-                } catch (OutOfResourcesException e) {
-                    Slog.e(TAG_WM, "Can't allocate drag surface w=" + width + " h=" + height, e);
-                    if (mDragState != null) {
-                        mDragState.reset();
-                        mDragState = null;
-                    }
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-
-        return token;
-    }
-
-    // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
 
@@ -4866,6 +4798,7 @@
         public static final int REPORT_WINDOWS_CHANGE = 19;
         public static final int DRAG_START_TIMEOUT = 20;
         public static final int DRAG_END_TIMEOUT = 21;
+
         public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
         public static final int BOOT_TIMEOUT = 23;
         public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
@@ -5120,47 +5053,12 @@
                     break;
                 }
 
-                case DRAG_START_TIMEOUT: {
-                    IBinder win = (IBinder)msg.obj;
-                    if (DEBUG_DRAG) {
-                        Slog.w(TAG_WM, "Timeout starting drag by win " + win);
-                    }
-                    synchronized (mWindowMap) {
-                        // !!! TODO: ANR the app that has failed to start the drag in time
-                        if (mDragState != null) {
-                            mDragState.unregister();
-                            mDragState.reset();
-                            mDragState = null;
-                        }
-                    }
-                    break;
-                }
-
-                case DRAG_END_TIMEOUT: {
-                    IBinder win = (IBinder)msg.obj;
-                    if (DEBUG_DRAG) {
-                        Slog.w(TAG_WM, "Timeout ending drag to win " + win);
-                    }
-                    synchronized (mWindowMap) {
-                        // !!! TODO: ANR the drag-receiving app
-                        if (mDragState != null) {
-                            mDragState.mDragResult = false;
-                            mDragState.endDragLw();
-                        }
-                    }
-                    break;
-                }
-
+                case DRAG_START_TIMEOUT:
+                case DRAG_END_TIMEOUT:
                 case TEAR_DOWN_DRAG_AND_DROP_INPUT: {
-                    if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag ending; tearing down input channel");
-                    DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
-                    if (interceptor != null) {
-                        synchronized (mWindowMap) {
-                            interceptor.tearDown();
-                        }
-                    }
+                    mDragDropController.handleMessage(WindowManagerService.this, msg);
+                    break;
                 }
-                break;
 
                 case REPORT_HARD_KEYBOARD_STATUS_CHANGE: {
                     notifyHardKeyboardStatusChange();
@@ -6532,9 +6430,16 @@
         }
     }
 
-    private void writeToProtoLocked(ProtoOutputStream proto) {
+    /**
+     * Write to a protocol buffer output stream. Protocol buffer message definition is at
+     * {@link com.android.server.wm.proto.WindowManagerServiceProto}.
+     *
+     * @param proto     Stream to write the WindowContainer object to.
+     * @param trim      If true, reduce the amount of data written.
+     */
+    private void writeToProtoLocked(ProtoOutputStream proto, boolean trim) {
         mPolicy.writeToProto(proto, POLICY);
-        mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER);
+        mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER, trim);
         if (mCurrentFocus != null) {
             mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
         }
@@ -6870,7 +6775,7 @@
         if (useProto) {
             final ProtoOutputStream proto = new ProtoOutputStream(fd);
             synchronized (mWindowMap) {
-                writeToProtoLocked(proto);
+                writeToProtoLocked(proto, false /* trim */);
             }
             proto.flush();
             return;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e171528..4370a76 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3120,9 +3120,9 @@
 
     @CallSuper
     @Override
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, WINDOW_CONTAINER);
+        super.writeToProto(proto, WINDOW_CONTAINER, trim);
         writeIdentifierToProto(proto, IDENTIFIER);
         proto.write(DISPLAY_ID, getDisplayId());
         proto.write(STACK_ID, getStackId());
@@ -3137,7 +3137,7 @@
         mWinAnimator.writeToProto(proto, ANIMATOR);
         proto.write(ANIMATING_EXIT, mAnimatingExit);
         for (int i = 0; i < mChildren.size(); i++) {
-            mChildren.get(i).writeToProto(proto, CHILD_WINDOWS);
+            mChildren.get(i).writeToProto(proto, CHILD_WINDOWS, trim);
         }
         proto.end(token);
     }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 943448e..62a2abb 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -267,13 +267,13 @@
 
     @CallSuper
     @Override
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, WINDOW_CONTAINER);
+        super.writeToProto(proto, WINDOW_CONTAINER, trim);
         proto.write(HASH_CODE, System.identityHashCode(this));
         for (int i = 0; i < mChildren.size(); i++) {
             final WindowState w = mChildren.get(i);
-            w.writeToProto(proto, WINDOWS);
+            w.writeToProto(proto, WINDOWS, trim);
         }
         proto.end(token);
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 9e4a9e9..cb13e85 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -51,11 +51,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityRecordTests extends ActivityTestsBase {
-    private final ComponentName testActivityComponent =
-            ComponentName.unflattenFromString("com.foo/.BarActivity");
-    private final ComponentName secondaryActivityComponent =
-            ComponentName.unflattenFromString("com.foo/.BarActivity2");
-
     private ActivityManagerService mService;
     private TestActivityStack mStack;
     private TaskRecord mTask;
@@ -69,8 +64,8 @@
         mService = createActivityManagerService();
         mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
-        mActivity = createActivity(mService, testActivityComponent, mTask);
+        mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+        mActivity = new ActivityBuilder(mService).setTask(mTask).build();
     }
 
     @Test
@@ -94,8 +89,8 @@
 
     @Test
     public void testNoCleanupMovingActivityInSameStack() throws Exception {
-        final TaskRecord newTask =
-                createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+        final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack)
+                .build();
         mActivity.reparent(newTask, 0, null /*reason*/);
         assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
     }
@@ -149,13 +144,13 @@
         mService.mSupportsMultiWindow = true;
 
         final TaskRecord task = taskPresent
-                ? createTask(mService.mStackSupervisor, testActivityComponent, mStack) : null;
+                ? new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build() : null;
 
         if (task != null) {
             task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
         }
 
-        final ActivityRecord record = createActivity(mService, secondaryActivityComponent, task);
+        final ActivityRecord record = new ActivityBuilder(mService).setTask(task).build();
         record.info.resizeMode = activityResizeable
                 ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 7f03034..fc17da8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -51,9 +51,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackSupervisorTests extends ActivityTestsBase {
-    private final ComponentName testActivityComponent =
-            ComponentName.unflattenFromString("com.foo/.BarActivity");
-
     private ActivityManagerService mService;
     private ActivityStackSupervisor mSupervisor;
     private ActivityStack mFullscreenStack;
@@ -87,15 +84,14 @@
      */
     @Test
     public void testReplacingTaskInPinnedStack() throws Exception {
-        final TaskRecord firstTask = createTask(
-                mSupervisor, testActivityComponent, mFullscreenStack);
-        final ActivityRecord firstActivity = createActivity(mService, testActivityComponent,
-                firstTask);
-        // Create a new task on the full screen stack
-        final TaskRecord secondTask = createTask(
-                mSupervisor, testActivityComponent, mFullscreenStack);
-        final ActivityRecord secondActivity = createActivity(mService, testActivityComponent,
-                secondTask);
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final TaskRecord firstTask = firstActivity.getTask();
+
+        final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final TaskRecord secondTask = secondActivity.getTask();
+
         mSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", mFullscreenStack);
 
         // Ensure full screen stack has both tasks.
@@ -142,10 +138,8 @@
      */
     @Test
     public void testStoppingActivityRemovedWhenResumed() throws Exception {
-        final TaskRecord firstTask = createTask(
-                mSupervisor, testActivityComponent, mFullscreenStack);
-        final ActivityRecord firstActivity = createActivity(mService, testActivityComponent,
-            firstTask);
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
         mSupervisor.mStoppingActivities.add(firstActivity);
 
         firstActivity.completeResumeLocked();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index e17e51b..480b210 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -23,7 +23,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -51,11 +53,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackTests extends ActivityTestsBase {
-    private static final ComponentName testActivityComponent =
-            ComponentName.unflattenFromString("com.foo/.BarActivity");
-    private static final ComponentName testOverlayComponent =
-            ComponentName.unflattenFromString("com.foo/.OverlayActivity");
-
     private ActivityManagerService mService;
     private ActivityStackSupervisor mSupervisor;
     private ActivityStack mStack;
@@ -70,7 +67,7 @@
         mSupervisor = mService.mStackSupervisor;
         mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask = createTask(mSupervisor, testActivityComponent, mStack);
+        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
     }
 
     @Test
@@ -82,7 +79,7 @@
 
     @Test
     public void testOccupiedTaskCleanupOnRemove() throws Exception {
-        final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
         assertNotNull(mTask.getWindowContainerController());
         mStack.removeTask(mTask, "testOccupiedTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
         assertNotNull(mTask.getWindowContainerController());
@@ -90,7 +87,7 @@
 
     @Test
     public void testNoPauseDuringResumeTopActivity() throws Exception {
-        final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
 
         // Simulate the a resumed activity set during
         // {@link ActivityStack#resumeTopActivityUncheckedLocked}.
@@ -108,7 +105,7 @@
 
     @Test
     public void testStopActivityWhenActivityDestroyed() throws Exception {
-        final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
         r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
         mSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", mStack);
         mStack.stopActivityLocked(r);
@@ -118,18 +115,23 @@
 
     @Test
     public void testFindTaskWithOverlay() throws Exception {
-        final ActivityRecord r = createActivity(mService, testActivityComponent, mTask, 0);
+        final ActivityRecord r = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(mStack)
+                .setUid(0)
+                .build();
+        final TaskRecord task = r.getTask();
         // Overlay must be for a different user to prevent recognizing a matching top activity
-        final ActivityRecord taskOverlay = createActivity(mService, testOverlayComponent, mTask,
-                UserHandle.PER_USER_RANGE * 2);
+        final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task)
+                .setUid(UserHandle.PER_USER_RANGE * 2).build();
         taskOverlay.mTaskOverlay = true;
 
         final ActivityStackSupervisor.FindTaskResult result =
                 new ActivityStackSupervisor.FindTaskResult();
         mStack.findTaskLocked(r, result);
 
-        assertEquals(mTask.getTopActivity(false /* includeOverlays */), r);
-        assertEquals(mTask.getTopActivity(true /* includeOverlays */), taskOverlay);
+        assertEquals(task.getTopActivity(false /* includeOverlays */), r);
+        assertEquals(task.getTopActivity(true /* includeOverlays */), taskOverlay);
         assertNotNull(result.r);
     }
 
@@ -245,9 +247,8 @@
     private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
             ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
         final T stack = display.createStack(windowingMode, activityType, onTop);
-        // Create a task and activity in the stack so that it has a top running activity.
-        final TaskRecord task = createTask(mSupervisor, testActivityComponent, stack);
-        final ActivityRecord r = createActivity(mService, testActivityComponent, task, 0);
+        final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
+                .setCreateTask(true).build();
         return stack;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 026abce..5b1e4b7 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -50,9 +50,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStarterTests extends ActivityTestsBase {
-    private static final ComponentName testActivityComponent =
-            ComponentName.unflattenFromString("com.foo/.BarActivity");
-
     private ActivityManagerService mService;
     private ActivityStarter mStarter;
 
@@ -66,9 +63,10 @@
     @Test
     public void testUpdateLaunchBounds() throws Exception {
         // When in a non-resizeable stack, the task bounds should be updated.
-        final TaskRecord task = createTask(mService.mStackSupervisor, testActivityComponent,
-                mService.mStackSupervisor.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+                .build();
         final Rect bounds = new Rect(10, 10, 100, 100);
 
         mStarter.updateBounds(task, bounds);
@@ -76,9 +74,10 @@
         assertEquals(task.getStack().mBounds, null);
 
         // When in a resizeable stack, the stack bounds should be updated as well.
-        final TaskRecord task2 = createTask(mService.mStackSupervisor, testActivityComponent,
-                mService.mStackSupervisor.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+                .build();
         assertTrue(task2.getStack() instanceof PinnedActivityStack);
         mStarter.updateBounds(task2, bounds);
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 07b49c5..198cc6d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static org.mockito.Mockito.mock;
@@ -76,62 +78,165 @@
     }
 
     protected ActivityManagerService createActivityManagerService() {
-        final ActivityManagerService service = spy(new TestActivityManagerService(mContext));
+        return setupActivityManagerService(new TestActivityManagerService(mContext));
+    }
+
+    protected ActivityManagerService setupActivityManagerService(ActivityManagerService service) {
+        service = spy(service);
         service.mWindowManager = prepareMockWindowManager();
         return service;
     }
 
-    protected static ActivityRecord createActivity(ActivityManagerService service,
-            ComponentName component, TaskRecord task) {
-        return createActivity(service, component, task, 0 /* userId */);
-    }
+    /**
+     * Builder for creating new activities.
+     */
+    protected static class ActivityBuilder {
+        // An id appended to the end of the component name to make it unique
+        private static int sCurrentActivityId = 0;
 
-    protected static ActivityRecord createActivity(ActivityManagerService service,
-            ComponentName component, TaskRecord task, int uid) {
-        Intent intent = new Intent();
-        intent.setComponent(component);
-        final ActivityInfo aInfo = new ActivityInfo();
-        aInfo.applicationInfo = new ApplicationInfo();
-        aInfo.applicationInfo.packageName = component.getPackageName();
-        aInfo.applicationInfo.uid = uid;
-        AttributeCache.init(service.mContext);
-        final ActivityRecord activity = new ActivityRecord(service, null /* caller */,
-                0 /* launchedFromPid */, 0, null, intent, null,
-                aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
-                0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
-                service.mStackSupervisor, null /* options */, null /* sourceRecord */);
-        activity.mWindowContainerController = mock(AppWindowContainerController.class);
+        // Default package name
+        private static final String DEFAULT_PACKAGE = "com.foo";
 
-        if (task != null) {
-            task.addActivityToTop(activity);
+        // Default base activity name
+        private static final String DEFAULT_BASE_ACTIVITY_NAME = ".BarActivity";
+
+        private final ActivityManagerService mService;
+
+        private ComponentName mComponent;
+        private TaskRecord mTaskRecord;
+        private int mUid;
+        private boolean mCreateTask;
+        private ActivityStack mStack;
+
+        ActivityBuilder(ActivityManagerService service) {
+            mService = service;
         }
 
-        return activity;
+        ActivityBuilder setComponent(ComponentName component) {
+            mComponent = component;
+            return this;
+        }
+
+        ActivityBuilder setTask(TaskRecord task) {
+            mTaskRecord = task;
+            return this;
+        }
+
+        ActivityBuilder setStack(ActivityStack stack) {
+            mStack = stack;
+            return this;
+        }
+
+        ActivityBuilder setCreateTask(boolean createTask) {
+            mCreateTask = createTask;
+            return this;
+        }
+
+        ActivityBuilder setUid(int uid) {
+            mUid = uid;
+            return this;
+        }
+
+        ActivityRecord build() {
+            if (mComponent == null) {
+                final int id = sCurrentActivityId++;
+                mComponent = ComponentName.createRelative(DEFAULT_PACKAGE,
+                        DEFAULT_BASE_ACTIVITY_NAME + id);
+            }
+
+            if (mCreateTask) {
+                mTaskRecord = new TaskBuilder(mService.mStackSupervisor)
+                        .setComponent(mComponent)
+                        .setStack(mStack).build();
+            }
+
+            Intent intent = new Intent();
+            intent.setComponent(mComponent);
+            final ActivityInfo aInfo = new ActivityInfo();
+            aInfo.applicationInfo = new ApplicationInfo();
+            aInfo.applicationInfo.packageName = mComponent.getPackageName();
+            aInfo.applicationInfo.uid = mUid;
+            AttributeCache.init(mService.mContext);
+            final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
+                    0 /* launchedFromPid */, 0, null, intent, null,
+                    aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
+                    0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
+                    mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
+            activity.mWindowContainerController = mock(AppWindowContainerController.class);
+
+            if (mTaskRecord != null) {
+                mTaskRecord.addActivityToTop(activity);
+            }
+
+            return activity;
+        }
     }
 
-    protected static TaskRecord createTask(ActivityStackSupervisor supervisor,
-            ComponentName component, ActivityStack stack) {
-        return createTask(supervisor, component, 0 /* flags */, 0 /* taskId */, stack);
-    }
+    /**
+     * Builder for creating new tasks.
+     */
+    protected static class TaskBuilder {
+        private final ActivityStackSupervisor mSupervisor;
 
-    protected static TaskRecord createTask(ActivityStackSupervisor supervisor,
-            ComponentName component, int flags, int taskId, ActivityStack stack) {
-        final ActivityInfo aInfo = new ActivityInfo();
-        aInfo.applicationInfo = new ApplicationInfo();
-        aInfo.applicationInfo.packageName = component.getPackageName();
+        private ComponentName mComponent;
+        private String mPackage;
+        private int mFlags = 0;
+        private int mTaskId = 0;
 
-        Intent intent = new Intent();
-        intent.setComponent(component);
-        intent.setFlags(flags);
+        private ActivityStack mStack;
 
-        final TaskRecord task = new TaskRecord(supervisor.mService, taskId, aInfo,
-                intent /*intent*/, null /*_taskDescription*/);
-        supervisor.setFocusStackUnchecked("test", stack);
-        stack.addTask(task, true, "creating test task");
-        task.setStack(stack);
-        task.setWindowContainerController(mock(TaskWindowContainerController.class));
+        TaskBuilder(ActivityStackSupervisor supervisor) {
+            mSupervisor = supervisor;
+        }
 
-        return task;
+        TaskBuilder setComponent(ComponentName component) {
+            mComponent = component;
+            return this;
+        }
+
+        TaskBuilder setPackage(String packageName) {
+            mPackage = packageName;
+            return this;
+        }
+
+        TaskBuilder setFlags(int flags) {
+            mFlags = flags;
+            return this;
+        }
+
+        TaskBuilder setTaskId(int taskId) {
+            mTaskId = taskId;
+            return this;
+        }
+
+        TaskBuilder setStack(ActivityStack stack) {
+            mStack = stack;
+            return this;
+        }
+
+        TaskRecord build() {
+            if (mStack == null) {
+                mStack = mSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+            }
+
+            final ActivityInfo aInfo = new ActivityInfo();
+            aInfo.applicationInfo = new ApplicationInfo();
+            aInfo.applicationInfo.packageName = mPackage;
+
+            Intent intent = new Intent();
+            intent.setComponent(mComponent);
+            intent.setFlags(mFlags);
+
+            final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
+                    intent /*intent*/, null /*_taskDescription*/);
+            mSupervisor.setFocusStackUnchecked("test", mStack);
+            mStack.addTask(task, true, "creating test task");
+            task.setStack(mStack);
+            task.setWindowContainerController(mock(TaskWindowContainerController.class));
+
+            return task;
+        }
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java
index 0007e8a..de831a0 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java
@@ -49,9 +49,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class LaunchingActivityPositionerTests extends ActivityTestsBase {
-    private final ComponentName testActivityComponent =
-            ComponentName.unflattenFromString("com.foo/.BarActivity");
-
     private LaunchingActivityPositioner mPositioner;
     private ActivityManagerService mService;
     private ActivityStack mStack;
@@ -73,8 +70,8 @@
 
         mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
-        mActivity = createActivity(mService, testActivityComponent, mTask);
+        mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+        mActivity = new ActivityBuilder(mService).setTask(mTask).build();
     }
 
 
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
index 0d64981c..30666c0 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
@@ -52,9 +52,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class LaunchingTaskPositionerTests extends ActivityTestsBase {
-    private final ComponentName testActivityComponent =
-            ComponentName.unflattenFromString("com.foo/.BarActivity");
-
     private final static int STACK_WIDTH = 100;
     private final static int STACK_HEIGHT = 200;
 
@@ -81,7 +78,7 @@
 
         // We must create the task after resizing to make sure it does not inherit the stack
         // dimensions on resize.
-        mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+        mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
 
         mPositioner = new LaunchingTaskPositioner();
 
@@ -193,11 +190,10 @@
         // wrap with try/finally to ensure cleanup of activity/stack.
         try {
             // empty tasks are ignored in conflicts
-            activity = createActivity(mService, testActivityComponent, mTask);
+            activity = new ActivityBuilder(mService).setTask(mTask).build();
 
             // Create secondary task
-            secondTask = createTask(mService.mStackSupervisor, testActivityComponent,
-                    mStack);
+            secondTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
 
             // layout second task
             assertEquals(RESULT_CONTINUE,
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index e607228..3c9b542 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -16,44 +16,55 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
 
+import static java.lang.Integer.MAX_VALUE;
+
+import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.RunningTaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
-import android.os.Debug;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.MutableLong;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
 import com.android.server.am.RecentTasks.Callbacks;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -67,13 +78,17 @@
     private static final int TEST_QUIET_USER_ID = 20;
     private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
     private static final UserInfo QUIET_USER_INFO = new UserInfo();
+    private static final ComponentName MY_COMPONENT = new ComponentName(
+            RecentTasksTest.class.getPackage().getName(), RecentTasksTest.class.getName());
     private static int LAST_TASK_ID = 1;
+    private static int INVALID_STACK_ID = 999;
 
     private Context mContext = InstrumentationRegistry.getContext();
     private ActivityManagerService mService;
     private ActivityStack mStack;
     private TestTaskPersister mTaskPersister;
     private RecentTasks mRecentTasks;
+    private RunningTasks mRunningTasks;
 
     private static ArrayList<TaskRecord> mTasks = new ArrayList<>();
     private static ArrayList<TaskRecord> mSameDocumentTasks = new ArrayList<>();
@@ -91,6 +106,14 @@
         }
 
         @Override
+        Set<Integer> getProfileIds(int userId) {
+            Set<Integer> profileIds = new HashSet<>();
+            profileIds.add(TEST_USER_0_ID);
+            profileIds.add(TEST_QUIET_USER_ID);
+            return profileIds;
+        }
+
+        @Override
         UserInfo getUserInfo(int userId) {
             switch (userId) {
                 case TEST_USER_0_ID:
@@ -108,12 +131,12 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityManagerService();
+        mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
+        mService = setupActivityManagerService(new MyTestActivityManagerService(mContext));
+        mRecentTasks = mService.getRecentTasks();
+        mRecentTasks.loadParametersFromResources(mContext.getResources());
         mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-        mRecentTasks = new RecentTasks(mService, mTaskPersister, new TestUserController(mService));
-        mRecentTasks.loadParametersFromResources(mContext.getResources());
         mCallbacksRecorder = new CallbacksRecorder();
         mRecentTasks.registerCallback(mCallbacksRecorder);
         QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
@@ -322,6 +345,103 @@
         assertTrimmed(mTasks.get(0), mTasks.get(1));
     }
 
+    @Test
+    public void testNotRecentsComponent_denyApiAccess() throws Exception {
+        doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
+                anyInt(), anyInt());
+
+        // Expect the following methods to fail due to recents component not being set
+        ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride(
+                TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
+        testRecentTasksApis(false /* expectNoSecurityException */);
+        // Don't throw for the following tests
+        ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride(TestRecentTasks.DENY);
+        testGetTasksApis(false /* expectNoSecurityException */);
+    }
+
+    @Test
+    public void testRecentsComponent_allowApiAccessWithoutPermissions() throws Exception {
+        doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
+                anyInt(), anyInt());
+
+        // Set the recents component and ensure that the following calls do not fail
+        ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride(TestRecentTasks.GRANT);
+        testRecentTasksApis(true /* expectNoSecurityException */);
+        testGetTasksApis(true /* expectNoSecurityException */);
+    }
+
+    private void testRecentTasksApis(boolean expectCallable) {
+        assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID));
+        assertSecurityException(expectCallable,
+                () -> mService.removeStacksInWindowingModes(new int[] {WINDOWING_MODE_UNDEFINED}));
+        assertSecurityException(expectCallable,
+                () -> mService.removeStacksWithActivityTypes(new int[] {ACTIVITY_TYPE_UNDEFINED}));
+        assertSecurityException(expectCallable, () -> mService.removeTask(0));
+        assertSecurityException(expectCallable,
+                () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
+        assertSecurityException(expectCallable,
+                () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
+        assertSecurityException(expectCallable,
+                () -> mService.moveTaskToDockedStack(0, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, true,
+                        true, new Rect()));
+        assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
+        assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
+        assertSecurityException(expectCallable,
+                () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+        assertSecurityException(expectCallable,
+                () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
+        assertSecurityException(expectCallable,
+                () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
+                        new Rect()));
+        assertSecurityException(expectCallable,
+                () -> mService.resizePinnedStack(new Rect(), new Rect()));
+        assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
+        assertSecurityException(expectCallable,
+                () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+        assertSecurityException(expectCallable, () -> {
+            try {
+                mService.getFocusedStackInfo();
+            } catch (RemoteException e) {
+                // Ignore
+            }
+        });
+        assertSecurityException(expectCallable,
+                () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
+        assertSecurityException(expectCallable,
+                () -> mService.startActivityFromRecents(0, new Bundle()));
+        assertSecurityException(expectCallable,
+                () -> mService.getTaskSnapshot(0, true));
+        assertSecurityException(expectCallable, () -> {
+            try {
+                mService.registerTaskStackListener(null);
+            } catch (RemoteException e) {
+                // Ignore
+            }
+        });
+        assertSecurityException(expectCallable, () -> {
+            try {
+                mService.unregisterTaskStackListener(null);
+            } catch (RemoteException e) {
+                // Ignore
+            }
+        });
+        assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
+        assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
+        assertSecurityException(expectCallable, () -> mService.cancelTaskThumbnailTransition(0));
+    }
+
+    private void testGetTasksApis(boolean expectCallable) {
+        mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
+        mService.getTasks(MAX_VALUE);
+        if (expectCallable) {
+            assertTrue(((TestRecentTasks) mRecentTasks).mLastAllowed);
+            assertTrue(((TestRunningTasks) mRunningTasks).mLastAllowed);
+        } else {
+            assertFalse(((TestRecentTasks) mRecentTasks).mLastAllowed);
+            assertFalse(((TestRunningTasks) mRunningTasks).mLastAllowed);
+        }
+    }
+
     private ComponentName createComponent(String className) {
         return new ComponentName(mContext.getPackageName(), className);
     }
@@ -335,8 +455,9 @@
     }
 
     private TaskRecord createTask(String className, int flags, int userId) {
-        TaskRecord task = createTask(mService.mStackSupervisor, createComponent(className), flags,
-                LAST_TASK_ID++, mStack);
+        TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+                .setComponent(createComponent(className))
+                .setStack(mStack).setFlags(flags).setTaskId(LAST_TASK_ID++).build();
         task.userId = userId;
         task.touchActiveTime();
         return task;
@@ -366,6 +487,55 @@
         }
     }
 
+    private void assertSecurityException(boolean expectCallable, Runnable runnable) {
+        boolean noSecurityException = true;
+        try {
+            runnable.run();
+        } catch (SecurityException se) {
+            noSecurityException = false;
+        } catch (Exception e) {
+            // We only care about SecurityExceptions, fall through here
+            e.printStackTrace();
+        }
+        if (noSecurityException != expectCallable) {
+            fail("Expected callable: " + expectCallable + " but got no security exception: "
+                    + noSecurityException);
+        }
+    }
+
+    private class MyTestActivityManagerService extends TestActivityManagerService {
+        MyTestActivityManagerService(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected ActivityStackSupervisor createStackSupervisor() {
+            return new MyTestActivityStackSupervisor(this, mHandlerThread.getLooper());
+        }
+
+        @Override
+        protected RecentTasks createRecentTasks() {
+            return new TestRecentTasks(this, mTaskPersister, new TestUserController(this));
+        }
+
+        @Override
+        public boolean isUserRunning(int userId, int flags) {
+            return true;
+        }
+    }
+
+    private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
+        public MyTestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
+            super(service, looper);
+        }
+
+        @Override
+        RunningTasks createRunningTasks() {
+            mRunningTasks = new TestRunningTasks();
+            return mRunningTasks;
+        }
+    }
+
     private static class CallbacksRecorder implements Callbacks {
         ArrayList<TaskRecord> added = new ArrayList<>();
         ArrayList<TaskRecord> trimmed = new ArrayList<>();
@@ -416,4 +586,61 @@
             return super.restoreTasksForUserLocked(userId, preaddedTasks);
         }
     }
+
+    private static class TestRecentTasks extends RecentTasks {
+        static final int GRANT = 0;
+        static final int DENY = 1;
+        static final int DENY_THROW_SECURITY_EXCEPTION = 2;
+
+        private boolean mOverrideIsCallerRecents;
+        private int mIsCallerRecentsPolicy;
+        boolean mLastAllowed;
+
+        TestRecentTasks(ActivityManagerService service, TaskPersister taskPersister,
+                UserController userController) {
+            super(service, taskPersister, userController);
+        }
+
+        @Override
+        boolean isCallerRecents(int callingUid) {
+            if (mOverrideIsCallerRecents) {
+                switch (mIsCallerRecentsPolicy) {
+                    case GRANT:
+                        return true;
+                    case DENY:
+                        return false;
+                    case DENY_THROW_SECURITY_EXCEPTION:
+                        throw new SecurityException();
+                }
+            }
+            return super.isCallerRecents(callingUid);
+        }
+
+        void setIsCallerRecentsOverride(int policy) {
+            mOverrideIsCallerRecents = true;
+            mIsCallerRecentsPolicy = policy;
+        }
+
+        @Override
+        ParceledListSlice<RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+                boolean getTasksAllowed,
+                boolean getDetailedTasks, int userId, int callingUid) {
+            mLastAllowed = getTasksAllowed;
+            return super.getRecentTasks(maxNum, flags, getTasksAllowed, getDetailedTasks, userId,
+                    callingUid);
+        }
+    }
+
+    private static class TestRunningTasks extends RunningTasks {
+        boolean mLastAllowed;
+
+        @Override
+        void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
+                int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
+                int callingUid, boolean allowed) {
+            mLastAllowed = allowed;
+            super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
+                    callingUid, allowed);
+        }
+    }
 }
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
new file mode 100644
index 0000000..fc75628
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RunningTasksTest extends ActivityTestsBase {
+
+    private Context mContext = InstrumentationRegistry.getContext();
+    private ActivityManagerService mService;
+
+    private RunningTasks mRunningTasks;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mService = createActivityManagerService();
+        mRunningTasks = new RunningTasks();
+    }
+
+    @Test
+    public void testCollectTasksByLastActiveTime() throws Exception {
+        // Create a number of stacks with tasks (of incrementing active time)
+        final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
+        final SparseArray<ActivityDisplay> displays = new SparseArray<>();
+        final ActivityDisplay display = new ActivityDisplay(supervisor, DEFAULT_DISPLAY);
+        displays.put(DEFAULT_DISPLAY, display);
+
+        final int numStacks = 2;
+        for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
+            final ActivityStack stack = new TestActivityStack(display, stackIndex, supervisor,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true);
+            display.addChild(stack, POSITION_BOTTOM);
+        }
+
+        final int numTasks = 10;
+        int activeTime = 0;
+        for (int i = 0; i < numTasks; i++) {
+            createTask(display.getChildAt(i % numStacks), ".Task" + i, i, activeTime++);
+        }
+
+        // Ensure that the latest tasks were returned in order of decreasing last active time,
+        // collected from all tasks across all the stacks
+        final int numFetchTasks = 5;
+        ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
+        mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
+                displays, -1 /* callingUid */, true /* allowed */);
+        assertTrue(tasks.size() == numFetchTasks);
+        for (int i = 0; i < numFetchTasks; i++) {
+            assertTrue(tasks.get(i).id == (numTasks - i - 1));
+        }
+
+        // Ensure that requesting more than the total number of tasks only returns the subset
+        // and does not crash
+        tasks.clear();
+        mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
+                displays, -1 /* callingUid */, true /* allowed */);
+        assertTrue(tasks.size() == numTasks);
+        for (int i = 0; i < numTasks; i++) {
+            assertTrue(tasks.get(i).id == (numTasks - i - 1));
+        }
+    }
+
+    /**
+     * Create a task with a single activity in it, with the given last active time.
+     */
+    private TaskRecord createTask(ActivityStack stack, String className, int taskId,
+            int lastActiveTime) {
+        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+                .setComponent(new ComponentName(mContext.getPackageName(), className))
+                .setTaskId(taskId)
+                .setStack(stack)
+                .build();
+        task.lastActiveTime = lastActiveTime;
+        final ActivityRecord activity = new ActivityBuilder(mService)
+                .setTask(task)
+                .setComponent(new ComponentName(mContext.getPackageName(), ".TaskActivity"))
+                .build();
+        return task;
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c2cb66d..27a29b6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -104,6 +104,8 @@
 import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.DefaultNetworkMetrics;
+import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
@@ -156,6 +158,9 @@
     private MockNetworkAgent mEthernetNetworkAgent;
     private Context mContext;
 
+    @Mock IpConnectivityMetrics.Logger mMetricsService;
+    @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+
     // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
     // do not go through ConnectivityService but talk to netd directly, so they don't automatically
     // reflect the state of our test ConnectivityService.
@@ -805,6 +810,11 @@
             return Context.ETHERNET_SERVICE.equals(name);
         }
 
+        @Override
+        protected IpConnectivityMetrics.Logger metricsLogger() {
+            return mMetricsService;
+        }
+
         public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
             return mLastCreatedNetworkMonitor;
         }
@@ -833,6 +843,9 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
 
+        MockitoAnnotations.initMocks(this);
+        when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics);
+
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
         // http://b/25897652 .
         if (Looper.myLooper() == null) {
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 2624176..ad6ebf9 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -198,21 +198,20 @@
 
     @Test
     public void testDefaultNetworkEventSerialization() {
-        ConnectivityMetricsEvent ev = describeIpEvent(
-                aType(DefaultNetworkEvent.class),
-                anInt(102),
-                anIntArray(1, 2, 3),
-                anInt(101),
-                aBool(true),
-                aBool(false));
+        DefaultNetworkEvent ev = new DefaultNetworkEvent();
+        ev.netId = 102;
+        ev.prevNetId = 101;
+        ev.transportTypes = new int[]{1, 2, 3};
+        ev.prevIPv4 = true;
+        ev.prevIPv6 = true;
 
         String want = String.join("\n",
                 "dropped_events: 0",
                 "events <",
                 "  if_name: \"\"",
                 "  link_layer: 0",
-                "  network_id: 0",
-                "  time_ms: 1",
+                "  network_id: 102",
+                "  time_ms: 0",
                 "  transports: 0",
                 "  default_network_event <",
                 "    default_network_duration_ms: 0",
@@ -226,7 +225,7 @@
                 "    previous_network_id <",
                 "      network_id: 101",
                 "    >",
-                "    previous_network_ip_support: 1",
+                "    previous_network_ip_support: 3",
                 "    transport_types: 1",
                 "    transport_types: 2",
                 "    transport_types: 3",
@@ -234,7 +233,7 @@
                 ">",
                 "version: 2\n");
 
-        verifySerialization(want, ev);
+        verifySerialization(want, IpConnectivityEventBuilder.toProto(ev));
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index a395c48..6c1decc 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
 import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -30,6 +31,10 @@
 import android.net.ConnectivityManager;
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.RouteInfo;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.metrics.ApfProgramEvent;
@@ -41,18 +46,22 @@
 import android.net.metrics.IpReachabilityEvent;
 import android.net.metrics.RaEvent;
 import android.net.metrics.ValidationProbeEvent;
-import android.system.OsConstants;
 import android.os.Parcelable;
 import android.support.test.runner.AndroidJUnit4;
+import android.system.OsConstants;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
+
+import com.android.internal.util.BitUtils;
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
+
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -162,6 +171,144 @@
     }
 
     @Test
+    public void testDefaultNetworkEvents() throws Exception {
+        final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+        final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+
+        NetworkAgentInfo[][] defaultNetworks = {
+            // nothing -> cell
+            {null, makeNai(100, 10, false, true, cell)},
+            // cell -> wifi
+            {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)},
+            // wifi -> nothing
+            {makeNai(101, 60, true, false, wifi), null},
+            // nothing -> cell
+            {null, makeNai(102, 10, true, true, cell)},
+            // cell -> wifi
+            {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
+        };
+
+        for (NetworkAgentInfo[] pair : defaultNetworks) {
+            mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(pair[1], pair[0]);
+        }
+
+        String want = String.join("\n",
+                "dropped_events: 0",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 100",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 100",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 0",
+                "    >",
+                "    previous_network_ip_support: 0",
+                "    transport_types: 0",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 101",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 101",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 100",
+                "    >",
+                "    previous_network_ip_support: 3",
+                "    transport_types: 1",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 0",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 0",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 101",
+                "    >",
+                "    previous_network_ip_support: 1",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 102",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 102",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 0",
+                "    >",
+                "    previous_network_ip_support: 0",
+                "    transport_types: 0",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 103",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 103",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 102",
+                "    >",
+                "    previous_network_ip_support: 3",
+                "    transport_types: 1",
+                "  >",
+                ">",
+                "version: 2\n");
+
+        verifySerialization(want, getdump("flush"));
+    }
+
+    @Test
     public void testEndToEndLogging() throws Exception {
         // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
@@ -194,7 +341,6 @@
         Parcelable[] events = {
             new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED),
             new DhcpClientEvent("SomeState", 192),
-            new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false),
             new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678),
             validationEv,
             apfStats,
@@ -233,6 +379,13 @@
         wakeupEvent("wlan0", 10008);
         wakeupEvent("rmnet0", 1000);
 
+        final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+        final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+        NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
+        NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
+        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(cellNai, null);
+        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(wifiNai, cellNai);
+
         String want = String.join("\n",
                 "dropped_events: 0",
                 "events <",
@@ -264,30 +417,6 @@
                 "  network_id: 0",
                 "  time_ms: 300",
                 "  transports: 0",
-                "  default_network_event <",
-                "    default_network_duration_ms: 0",
-                "    final_score: 0",
-                "    initial_score: 0",
-                "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 102",
-                "    >",
-                "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 101",
-                "    >",
-                "    previous_network_ip_support: 1",
-                "    transport_types: 1",
-                "    transport_types: 2",
-                "    transport_types: 3",
-                "  >",
-                ">",
-                "events <",
-                "  if_name: \"\"",
-                "  link_layer: 4",
-                "  network_id: 0",
-                "  time_ms: 400",
-                "  transports: 0",
                 "  ip_provisioning_event <",
                 "    event_type: 1",
                 "    if_name: \"\"",
@@ -298,7 +427,7 @@
                 "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 0",
-                "  time_ms: 500",
+                "  time_ms: 400",
                 "  transports: 0",
                 "  validation_probe_event <",
                 "    latency_ms: 40730",
@@ -310,7 +439,7 @@
                 "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 0",
-                "  time_ms: 600",
+                "  time_ms: 500",
                 "  transports: 0",
                 "  apf_statistics <",
                 "    dropped_ras: 2",
@@ -331,7 +460,7 @@
                 "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 0",
-                "  time_ms: 700",
+                "  time_ms: 600",
                 "  transports: 0",
                 "  ra_event <",
                 "    dnssl_lifetime: -1",
@@ -344,6 +473,50 @@
                 ">",
                 "events <",
                 "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 100",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 100",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 0",
+                "    >",
+                "    previous_network_ip_support: 0",
+                "    transport_types: 0",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 101",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 101",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 100",
+                "    >",
+                "    previous_network_ip_support: 2",
+                "    transport_types: 1",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 100",
                 "  time_ms: 0",
@@ -471,6 +644,26 @@
         mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
     }
 
+    NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) {
+        NetworkAgentInfo nai = mock(NetworkAgentInfo.class);
+        when(nai.network()).thenReturn(new Network(netId));
+        when(nai.getCurrentScore()).thenReturn(score);
+        nai.linkProperties = new LinkProperties();
+        nai.networkCapabilities = new NetworkCapabilities();
+        for (int t : BitUtils.unpackBits(transports)) {
+            nai.networkCapabilities.addTransportType(t);
+        }
+        if (ipv4) {
+            nai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.12/24"));
+            nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0")));
+        }
+        if (ipv6) {
+            nai.linkProperties.addLinkAddress(new LinkAddress("2001:db8:dead:beef:f00::a0/64"));
+            nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("::/0")));
+        }
+        return nai;
+    }
+
     List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
         ArgumentCaptor<ConnectivityMetricsEvent> captor =
                 ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);