Merge "Use session id to uniquely identidy autofill ids for multi-session."
diff --git a/api/current.txt b/api/current.txt
index d3ef74f..e31f8a0 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -5680,6 +5680,7 @@
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
     method public boolean areNotificationsEnabled();
+    method public boolean canNotifyAsPackage(java.lang.String);
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
@@ -5698,13 +5699,17 @@
     method public android.app.NotificationChannelGroup getNotificationChannelGroup(java.lang.String);
     method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
     method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
+    method public java.lang.String getNotificationDelegate();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationListenerAccessGranted(android.content.ComponentName);
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
+    method public void notifyAsPackage(java.lang.String, java.lang.String, int, android.app.Notification);
     method public boolean removeAutomaticZenRule(java.lang.String);
+    method public void revokeNotificationDelegate();
     method public final void setInterruptionFilter(int);
+    method public void setNotificationDelegate(java.lang.String);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_APP_BLOCK_STATE_CHANGED = "android.app.action.APP_BLOCK_STATE_CHANGED";
@@ -13978,6 +13983,7 @@
     method public float getFontSpacing();
     method public java.lang.String getFontVariationSettings();
     method public int getHinting();
+    method public int getHyphenEdit();
     method public float getLetterSpacing();
     method public android.graphics.MaskFilter getMaskFilter();
     method public int getOffsetForAdvance(char[], int, int, int, int, boolean, float);
@@ -14041,6 +14047,7 @@
     method public void setFontFeatureSettings(java.lang.String);
     method public boolean setFontVariationSettings(java.lang.String);
     method public void setHinting(int);
+    method public void setHyphenEdit(int);
     method public void setLetterSpacing(float);
     method public void setLinearText(boolean);
     method public android.graphics.MaskFilter setMaskFilter(android.graphics.MaskFilter);
@@ -15237,7 +15244,9 @@
     field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320
     field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8
     field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c
+    field public static final int FONT_WEIGHT_MAX = 1000; // 0x3e8
     field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
+    field public static final int FONT_WEIGHT_MIN = 1; // 0x1
     field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
     field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258
     field public static final int FONT_WEIGHT_THIN = 100; // 0x64
@@ -39652,10 +39661,12 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
+    method public java.lang.String getOpPkg();
     method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
     method public java.lang.String getTag();
+    method public int getUid();
     method public android.os.UserHandle getUser();
     method public deprecated int getUserId();
     method public boolean isClearable();
@@ -43497,6 +43508,22 @@
     method public abstract void handleTag(boolean, java.lang.String, android.text.Editable, org.xml.sax.XMLReader);
   }
 
+  public class Hyphenator {
+    method public static int packHyphenEdit(int, int);
+    method public static int unpackEndHyphenEdit(int);
+    method public static int unpackStartHyphenEdit(int);
+    field public static final int END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN = 3; // 0x3
+    field public static final int END_HYPHEN_EDIT_INSERT_HYPHEN = 2; // 0x2
+    field public static final int END_HYPHEN_EDIT_INSERT_MAQAF = 4; // 0x4
+    field public static final int END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN = 5; // 0x5
+    field public static final int END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN = 6; // 0x6
+    field public static final int END_HYPHEN_EDIT_NO_EDIT = 0; // 0x0
+    field public static final int END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN = 1; // 0x1
+    field public static final int START_HYPHEN_EDIT_INSERT_HYPHEN = 1; // 0x1
+    field public static final int START_HYPHEN_EDIT_INSERT_ZWJ = 2; // 0x2
+    field public static final int START_HYPHEN_EDIT_NO_EDIT = 0; // 0x0
+  }
+
   public abstract interface InputFilter {
     method public abstract java.lang.CharSequence filter(java.lang.CharSequence, int, int, android.text.Spanned, int, int);
   }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 3171e3e..4f004d93 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -165,4 +165,9 @@
     void applyRestore(in byte[] payload, int user);
 
     ParceledListSlice getAppActiveNotifications(String callingPkg, int userId);
+
+    void setNotificationDelegate(String callingPkg, String delegate);
+    void revokeNotificationDelegate(String callingPkg);
+    String getNotificationDelegate(String callingPkg);
+    boolean canNotifyAsPackage(String callingPkg, String targetPkg);
 }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 4b25b8b..b96b39d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -352,7 +353,7 @@
     }
 
     /**
-     * Post a notification to be shown in the status bar. If a notification with
+     * Posts a notification to be shown in the status bar. If a notification with
      * the same tag and id has already been posted by your application and has not yet been
      * canceled, it will be replaced by the updated information.
      *
@@ -376,6 +377,42 @@
     }
 
     /**
+     * Posts a notification as a specified package to be shown in the status bar. If a notification
+     * with the same tag and id has already been posted for that package and has not yet been
+     * canceled, it will be replaced by the updated information.
+     *
+     * All {@link android.service.notification.NotificationListenerService listener services} will
+     * be granted {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} access to any {@link Uri uris}
+     * provided on this notification or the
+     * {@link NotificationChannel} this notification is posted to using
+     * {@link Context#grantUriPermission(String, Uri, int)}. Permission will be revoked when the
+     * notification is canceled, or you can revoke permissions with
+     * {@link Context#revokeUriPermission(Uri, int)}.
+     *
+     * @param targetPackage The package to post the notification as. The package must have granted
+     *                      you access to post notifications on their behalf with
+     *                      {@link #setNotificationDelegate(String)}.
+     * @param tag A string identifier for this notification.  May be {@code null}.
+     * @param id An identifier for this notification.  The pair (tag, id) must be unique
+     *        within your application.
+     * @param notification A {@link Notification} object describing what to
+     *        show the user. Must not be null.
+     */
+    public void notifyAsPackage(@NonNull String targetPackage, @NonNull String tag, int id,
+            Notification notification) {
+        INotificationManager service = getService();
+        String sender = mContext.getPackageName();
+
+        try {
+            if (localLOGV) Log.v(TAG, sender + ": notify(" + id + ", " + notification + ")");
+            service.enqueueNotificationWithTag(targetPackage, sender, tag, id,
+                    fixNotification(notification), mContext.getUser().getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide
      */
     @UnsupportedAppUsage
@@ -383,6 +420,18 @@
     {
         INotificationManager service = getService();
         String pkg = mContext.getPackageName();
+
+        try {
+            if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
+            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
+                    fixNotification(notification), user.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private Notification fixNotification(Notification notification) {
+        String pkg = mContext.getPackageName();
         // Fix the notification as best we can.
         Notification.addFieldsFromContext(mContext, notification);
 
@@ -400,19 +449,12 @@
                         + notification);
             }
         }
-        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
+
         notification.reduceImageSizes(mContext);
 
         ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         boolean isLowRam = am.isLowRamDevice();
-        final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam,
-                mContext);
-        try {
-            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
-                    copy, user.getIdentifier());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return Builder.maybeCloneStrippedForDelivery(notification, isLowRam, mContext);
     }
 
     private void fixLegacySmallIcon(Notification n, String pkg) {
@@ -474,6 +516,72 @@
     }
 
     /**
+     * Allows a package to post notifications on your behalf using
+     * {@link #notifyAsPackage(String, String, int, Notification)}.
+     *
+     * This can be used to allow persistent processes to post notifications based on messages
+     * received on your behalf from the cloud, without your process having to wake up.
+     *
+     * You can check if you have an allowed delegate with {@link #getNotificationDelegate()} and
+     * revoke your delegate with {@link #revokeNotificationDelegate()}.
+     *
+     * @param delegate Package name of the app which can send notifications on your behalf.
+     */
+    public void setNotificationDelegate(@NonNull String delegate) {
+        INotificationManager service = getService();
+        String pkg = mContext.getPackageName();
+        if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
+        try {
+            service.setNotificationDelegate(pkg, delegate);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Revokes permission for your {@link #setNotificationDelegate(String) notification delegate}
+     * to post notifications on your behalf.
+     */
+    public void revokeNotificationDelegate() {
+        INotificationManager service = getService();
+        String pkg = mContext.getPackageName();
+        try {
+            service.revokeNotificationDelegate(pkg);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the {@link #setNotificationDelegate(String) delegate} that can post notifications on
+     * your behalf, if there currently is one.
+     */
+    public @Nullable String getNotificationDelegate() {
+        INotificationManager service = getService();
+        String pkg = mContext.getPackageName();
+        try {
+            return service.getNotificationDelegate(pkg);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether you are allowed to post notifications on behalf of a given package, with
+     * {@link #notifyAsPackage(String, String, int, Notification)}.
+     *
+     * See {@link #setNotificationDelegate(String)}.
+     */
+    public boolean canNotifyAsPackage(String pkg) {
+        INotificationManager service = getService();
+        try {
+            return service.canNotifyAsPackage(mContext.getPackageName(), pkg);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates a group container for {@link NotificationChannel} objects.
      *
      * This can be used to rename an existing group.
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index dd97d52..84826e0 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -18,7 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.app.Notification;
-import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -261,7 +261,7 @@
         return this.user.getIdentifier();
     }
 
-    /** The package of the app that posted the notification. */
+    /** The package that the notification belongs to. */
     public String getPackageName() {
         return pkg;
     }
@@ -277,14 +277,18 @@
         return tag;
     }
 
-    /** The notifying app's calling uid. @hide */
-    @UnsupportedAppUsage
+    /**
+     * The notifying app's ({@link #getPackageName()}'s) uid.
+     */
     public int getUid() {
         return uid;
     }
 
-    /** The package used for AppOps tracking. @hide */
-    @UnsupportedAppUsage
+    /** The package that posted the notification.
+     *<p>
+     * Might be different from {@link #getPackageName()} if the app owning the notification has
+     * a {@link NotificationManager#setNotificationDelegate(String) notification delegate}.
+     */
     public String getOpPkg() {
         return opPkg;
     }
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 4f1488e..e4200ac 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,15 +16,145 @@
 
 package android.text;
 
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
- * Hyphenator just initializes the native implementation of automatic hyphenation,
- * in essence finding valid hyphenation opportunities in a word.
+ * Provides constants and pack/unpack methods for the HyphenEdit.
  *
- * @hide
+ * Hyphenator provides constant values for start of line and end of line modification.
+ * For example, by passing {@link #END_HYPHEN_EDIT_INSERT_HYPHEN} like as follows, HYPHEN(U+2010)
+ * character is appended at the end of line.
+ *
+ * <pre>
+ * <code>
+ *   Paint paint = new Paint();
+ *   paint.setHyphenEdit(Hyphenator.packHyphenEdit(
+ *       Hyphenator.START_HYPHEN_EDIT_NO_EDIT,
+ *       Hyphenator.END_HYPHEN_EDIT_INSERT_HYPHEN));
+ *   paint.measureText("abc", 0, 3);  // Returns the width of "abc‐"
+ *   Canvas.drawText("abc", 0, 3, 0f, 0f, paint);  // Draws "abc‐"
+ * </code>
+ * </pre>
+ *
+ * @see android.graphics.Paint#setHyphenEdit(int)
  */
 public class Hyphenator {
+    private Hyphenator() {}
+
+    /** @hide */
+    @IntDef(prefix = { "START_HYPHEN_EDIT_" }, value = {
+        START_HYPHEN_EDIT_NO_EDIT,
+        START_HYPHEN_EDIT_INSERT_HYPHEN,
+        START_HYPHEN_EDIT_INSERT_ZWJ
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StartHyphenEdit {}
+
+    /**
+     * An integer representing the starting of the line has no modification for hyphenation.
+     */
+    public static final int START_HYPHEN_EDIT_NO_EDIT = 0x00;
+
+    /**
+     * An integer representing the starting of the line has normal hyphen character (U+002D).
+     */
+    public static final int START_HYPHEN_EDIT_INSERT_HYPHEN = 0x01;
+
+    /**
+     * An integer representing the starting of the line has Zero-Width-Joiner (U+200D).
+     */
+    public static final int START_HYPHEN_EDIT_INSERT_ZWJ = 0x02;
+
+    /** @hide */
+    @IntDef(prefix = { "END_HYPHEN_EDIT_" }, value = {
+        END_HYPHEN_EDIT_NO_EDIT,
+        END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN,
+        END_HYPHEN_EDIT_INSERT_HYPHEN,
+        END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN,
+        END_HYPHEN_EDIT_INSERT_MAQAF,
+        END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN,
+        END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EndHyphenEdit {}
+
+    /**
+     * An integer representing the end of the line has no modification for hyphenation.
+     */
+    public static final int END_HYPHEN_EDIT_NO_EDIT = 0x00;
+
+    /**
+     * An integer representing the character at the end of the line is replaced with hyphen
+     * character (U+002D).
+     */
+    public static final int END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN = 0x01;
+
+    /**
+     * An integer representing the end of the line has normal hyphen character (U+002D).
+     */
+    public static final int END_HYPHEN_EDIT_INSERT_HYPHEN = 0x02;
+
+    /**
+     * An integer representing the end of the line has Armentian hyphen (U+058A).
+     */
+    public static final int END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN = 0x03;
+
+    /**
+     * An integer representing the end of the line has maqaf (Hebrew hyphen, U+05BE).
+     */
+    public static final int END_HYPHEN_EDIT_INSERT_MAQAF = 0x04;
+
+    /**
+     * An integer representing the end of the line has Canadian Syllabics hyphen (U+1400).
+     */
+    public static final int END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN = 0x05;
+
+    /**
+     * An integer representing the end of the line has Zero-Width-Joiner (U+200D) followed by normal
+     * hyphen character (U+002D).
+     */
+    public static final int END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN = 0x06;
+
+    // Following three constants are used for packing start hyphen edit and end hyphen edit into
+    // single integer. Following encodings must be the same as the minikin's one.
+    // See frameworks/minikin/include/Hyphenator.h for more details.
+    private static final int END_HYPHEN_EDIT_MASK = 0x07;  // 0b00111
+    private static final int START_HYPHEN_EDIT_MASK = 0x18;  // 0b11000
+    private static final int START_HYPHEN_EDIT_SHIFT = 0x03;
+
+    /**
+     * Extract start hyphen edit from packed value.
+     */
+    public static @StartHyphenEdit int unpackStartHyphenEdit(int hyphenEdit) {
+        return (hyphenEdit & START_HYPHEN_EDIT_MASK) >> START_HYPHEN_EDIT_SHIFT;
+    }
+
+    /**
+     * Extract end hyphen edit from packed value.
+     */
+    public static @EndHyphenEdit int unpackEndHyphenEdit(int hyphenEdit) {
+        return hyphenEdit & END_HYPHEN_EDIT_MASK;
+    }
+
+    /**
+     * Pack the start hyphen edit and end hyphen edit into single integer.
+     */
+    public static int packHyphenEdit(@StartHyphenEdit int startHyphenEdit,
+            @EndHyphenEdit int endHyphenEdit) {
+        return ((startHyphenEdit << START_HYPHEN_EDIT_SHIFT) & START_HYPHEN_EDIT_MASK)
+                | (endHyphenEdit & END_HYPHEN_EDIT_MASK);
+    }
+
+
+    /**
+     * @hide
+     */
     public static void init() {
         nInit();
     }
+
     private static native void nInit();
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 128f860..5adb1ca 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1216,11 +1216,19 @@
     }
 
     /**
+     * Returns the packed hyphen edit value for this line.
+     *
+     * You can extract start hyphen edit and end hyphen edit by using
+     * {@link Hyphenator#unpackStartHyphenEdit(int)} and
+     * {@link Hyphenator#unpackEndHyphenEdit(int)}.
+     *
+     * @param lineNumber a line number
+     * @return A packed hyphen edit value.
      * @hide
      */
     @Override
-    public int getHyphen(int line) {
-        return mLines[mColumns * line + HYPHEN] & HYPHEN_MASK;
+    public int getHyphen(int lineNumber) {
+        return mLines[mColumns * lineNumber + HYPHEN] & HYPHEN_MASK;
     }
 
     /**
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 9667b10..bf2d600 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -1053,13 +1053,16 @@
         return runIsRtl ? -ret : ret;
     }
 
-    private int adjustHyphenEdit(int start, int limit, int hyphenEdit) {
-        int result = hyphenEdit;
+    private int adjustHyphenEdit(int start, int limit, int packedHyphenEdit) {
+        int result = packedHyphenEdit;
         // Only draw hyphens on first or last run in line. Disable them otherwise.
         if (start > 0) { // not the first run
-            result &= ~Paint.HYPHENEDIT_MASK_START_OF_LINE;
+            result = Hyphenator.packHyphenEdit(Hyphenator.START_HYPHEN_EDIT_NO_EDIT,
+                    Hyphenator.unpackEndHyphenEdit(packedHyphenEdit));
         }
         if (limit < mLen) { // not the last run
+            result = Hyphenator.packHyphenEdit(Hyphenator.unpackStartHyphenEdit(packedHyphenEdit),
+                    Hyphenator.END_HYPHEN_EDIT_NO_EDIT);
             result &= ~Paint.HYPHENEDIT_MASK_END_OF_LINE;
         }
         return result;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1b412a7..0645a16 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -63,6 +63,7 @@
 import android.graphics.RectF;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.graphics.fonts.Font;
 import android.graphics.fonts.FontVariationAxis;
 import android.icu.text.DecimalFormatSymbols;
 import android.os.AsyncTask;
@@ -2068,7 +2069,7 @@
      */
     private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,
             @XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,
-            @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+            @IntRange(from = -1, to = Font.FONT_WEIGHT_MAX) int weight) {
         if (typeface == null && familyName != null) {
             // Lookup normal Typeface from system font map.
             final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
@@ -2095,9 +2096,9 @@
     }
 
     private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,
-            @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+            @IntRange(from = -1, to = Font.FONT_WEIGHT_MAX) int weight) {
         if (weight >= 0) {
-            weight = Math.min(Typeface.MAX_WEIGHT, weight);
+            weight = Math.min(Font.FONT_WEIGHT_MAX, weight);
             final boolean italic = (style & Typeface.ITALIC) != 0;
             setTypeface(Typeface.create(typeface, weight, italic));
         } else {
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 7fc1787..6f30653 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1742,26 +1742,52 @@
     }
 
     /**
-     * Get the current value of hyphen edit.
+     * Get the current value of packed hyphen edit.
+     *
+     * You can extract start hyphen edit and end hyphen edit by using
+     * {@link android.text.Hyphenator#unpackStartHyphenEdit(int)} and
+     * {@link android.text.Hyphenator#unpackEndHyphenEdit(int)}.
+     *
+     * The default value is 0 which is equivalent to packed value of
+     * {@link android.text.Hyphenator#START_HYPHEN_EDIT_NO_EDIT} and
+     * {@link android.text.Hyphenator#END_HYPHEN_EDIT_NO_EDIT}.
      *
      * @return the current hyphen edit value
-     *
-     * @hide
+     * @see #setHyphenEdit(int)
      */
     public int getHyphenEdit() {
         return nGetHyphenEdit(mNativePaint);
     }
 
     /**
-     * Set a hyphen edit on the paint (causes a hyphen to be added to text when
-     * measured or drawn).
+     * Set a packed hyphen edit on the paint.
      *
-     * @param hyphen 0 for no edit, 1 for adding a hyphen at the end, etc.
-     *        Definition of various values are in the HyphenEdit class in Minikin's Hyphenator.h.
+     * By setting hyphen edit, the measurement and drawing is performed with modifying hyphenation
+     * at the start of line and end of line. For example, by passing
+     * {@link android.text.Hyphenator#END_HYPHEN_EDIT_INSERT_HYPHEN} like as follows, HYPHEN(U+2010)
+     * character is appended at the end of line.
      *
-     * @hide
+     * <pre>
+     * <code>
+     *   Paint paint = new Paint();
+     *   paint.setHyphenEdit(Hyphenator.packHyphenEdit(
+     *       Hyphenator.START_HYPHEN_EDIT_NO_EDIT,
+     *       Hyphenator.END_HYPHEN_EDIT_INSERT_HYPHEN));
+     *   paint.measureText("abc", 0, 3);  // Returns the width of "abc‐"
+     *   Canvas.drawText("abc", 0, 3, 0f, 0f, paint);  // Draws "abc‐"
+     * </code>
+     * </pre>
+     *
+     * You can pack start hyphen edit and end hyphen edit by
+     * {@link android.text.Hyphenator#packHyphenEdit(int,int)}
+     *
+     * The default value is 0 which is equivalent to packed value of
+     * {@link android.text.Hyphenator#START_HYPHEN_EDIT_NO_EDIT} and
+     * {@link android.text.Hyphenator#END_HYPHEN_EDIT_NO_EDIT}.
+     *
+     * @param hyphen a packed hyphen edit value.
+     * @see #getHyphenEdit()
      */
-    @UnsupportedAppUsage
     public void setHyphenEdit(int hyphen) {
         nSetHyphenEdit(mNativePaint, hyphen);
     }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 4388411..5aa09ce 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -148,13 +148,7 @@
     @UnsupportedAppUsage
     private @Style int mStyle = 0;
 
-    /**
-     * A maximum value for the weight value.
-     * @hide
-     */
-    public static final int MAX_WEIGHT = 1000;
-
-    private @IntRange(from = 0, to = MAX_WEIGHT) int mWeight = 0;
+    private @IntRange(from = 0, to = android.graphics.fonts.Font.FONT_WEIGHT_MAX) int mWeight = 0;
 
     // Value for weight and italic. Indicates the value is resolved by font metadata.
     // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index a99d297..8aa4845 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -51,6 +51,11 @@
     private static final int STYLE_NORMAL = 0;
 
     /**
+     * A minimum weight value for the font
+     */
+    public static final int FONT_WEIGHT_MIN = 1;
+
+    /**
      * A font weight value for the thin weight
      */
     public static final int FONT_WEIGHT_THIN = 100;
@@ -96,6 +101,11 @@
     public static final int FONT_WEIGHT_BLACK = 900;
 
     /**
+     * A maximum weight value for the font
+     */
+    public static final int FONT_WEIGHT_MAX = 1000;
+
+    /**
      * A builder class for creating new Font.
      */
     public static class Builder {
@@ -322,8 +332,9 @@
          * @param weight a weight value
          * @return this builder
          */
-        public @NonNull Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
-            Preconditions.checkArgument(1 <= weight && weight <= 1000);
+        public @NonNull Builder setWeight(
+                @IntRange(from = FONT_WEIGHT_MIN, to = FONT_WEIGHT_MAX) int weight) {
+            Preconditions.checkArgument(FONT_WEIGHT_MIN <= weight && weight <= FONT_WEIGHT_MAX);
             mWeight = weight;
             return this;
         }
@@ -403,6 +414,7 @@
                     mItalic = STYLE_NORMAL;
                 }
             }
+            mWeight = Math.max(FONT_WEIGHT_MIN, Math.min(FONT_WEIGHT_MAX, mWeight));
             final boolean italic = (mItalic == STYLE_ITALIC);
             final long builderPtr = nInitBuilder();
             if (mAxes != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 689669f..caea04f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -32,11 +32,10 @@
 import java.util.List;
 
 /**
- * MapProfile handles Bluetooth MAP profile.
+ * MapProfile handles the Bluetooth MAP MSE role
  */
 public class MapProfile implements LocalBluetoothProfile {
     private static final String TAG = "MapProfile";
-    private static boolean V = true;
 
     private BluetoothMap mService;
     private boolean mIsProfileReady;
@@ -59,7 +58,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
+            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothMap) proxy;
             // We just bound to the service, so refresh the UI for any connected MAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,14 +80,14 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            Log.d(TAG, "Bluetooth service disconnected");
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
     }
 
     public boolean isProfileReady() {
-        if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+        Log.d(TAG, "isProfileReady(): " + mIsProfileReady);
         return mIsProfileReady;
     }
 
@@ -114,45 +113,45 @@
     }
 
     public boolean connect(BluetoothDevice device) {
-        if(V)Log.d(TAG,"connect() - should not get called");
+        Log.d(TAG, "connect() - should not get called");
         return false; // MAP never connects out
     }
 
     public boolean disconnect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-        if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
-            if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
-            }
-            return mService.disconnect(device);
-        } else {
+        if (mService == null) {
             return false;
         }
+        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        }
+        return mService.disconnect(device);
     }
 
     public int getConnectionStatus(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
-        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-        if(V) Log.d(TAG,"getConnectionStatus: status is: "+ mService.getConnectionState(device));
-
-        return !deviceList.isEmpty() && deviceList.get(0).equals(device)
-                ? mService.getConnectionState(device)
-                : BluetoothProfile.STATE_DISCONNECTED;
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+        return mService.getConnectionState(device);
     }
 
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
     }
 
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return BluetoothProfile.PRIORITY_OFF;
+        }
         return mService.getPriority(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
-        if (mService == null) return;
+        if (mService == null) {
+            return;
+        }
         if (preferred) {
             if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
                 mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -163,7 +162,9 @@
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
-        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        if (mService == null) {
+            return new ArrayList<BluetoothDevice>(0);
+        }
         return mService.getDevicesMatchingConnectionStates(
               new int[] {BluetoothProfile.STATE_CONNECTED,
                          BluetoothProfile.STATE_CONNECTING,
@@ -201,7 +202,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index ed06752..304a00f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -495,32 +495,37 @@
     }
 
     @Override
-    public void onUserSwitched(int newUserId) {
-        super.onUserSwitched(newUserId);
-        if (mFullscreenUserSwitcher != null) {
-            mFullscreenUserSwitcher.onUserSwitched(newUserId);
-        }
-    }
-
-    @Override
     public void onStateChanged(int newState) {
         super.onStateChanged(newState);
-        CarUserManagerHelper helper = new CarUserManagerHelper(mContext);
-        if (!helper.isHeadlessSystemUser()) {
-            showUserSwitcher();
+        if (newState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
+            if (!mFullscreenUserSwitcher.isVisible()) {
+                // Current execution path continues to set state after this, thus we deffer the
+                // dismissal to the next execution cycle.
+                postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible.
+            }
+        } else {
+            mFullscreenUserSwitcher.hide();
         }
     }
 
     public void showUserSwitcher() {
-        if (mFullscreenUserSwitcher != null) {
-            if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
-                mFullscreenUserSwitcher.show();
-            } else {
-                mFullscreenUserSwitcher.hide();
-            }
+        if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
+            mFullscreenUserSwitcher.show(); // Makes the switcher visible.
         }
     }
 
+    public void postDismissKeyguard() {
+        mHandler.post(this::dismissKeyguard);
+    }
+
+    /**
+     * Dismisses the keyguard and shows bouncer if authentication is necessary.
+     */
+    public void dismissKeyguard() {
+        executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
+            true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+    }
+
     @Override
     public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
         // Do nothing, we don't want to display media art in the lock screen for a car.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 67a76fd..2ebf5eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,7 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.car.user.CarUserManagerHelper;
 import android.content.Context;
 import android.view.View;
 import android.view.ViewStub;
@@ -26,115 +25,87 @@
 import androidx.recyclerview.widget.GridLayoutManager;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
  * Manages the fullscreen user switcher.
  */
 public class FullscreenUserSwitcher {
-    private final View mContainer;
-    private final View mParent;
     private final UserGridRecyclerView mUserGridView;
+    private final View mParent;
     private final int mShortAnimDuration;
-    private final StatusBar mStatusBar;
-    private final CarUserManagerHelper mCarUserManagerHelper;
-    private boolean mShowing;
+    private final CarStatusBar mStatusBar;
 
-    public FullscreenUserSwitcher(StatusBar statusBar, ViewStub containerStub, Context context) {
+    public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
         mStatusBar = statusBar;
         mParent = containerStub.inflate();
-        mContainer = mParent.findViewById(R.id.container);
-        mUserGridView = mContainer.findViewById(R.id.user_grid);
+        mParent.setVisibility(View.VISIBLE);
+        View container = mParent.findViewById(R.id.container);
+
+        // Initialize user grid.
+        mUserGridView = container.findViewById(R.id.user_grid);
         GridLayoutManager layoutManager = new GridLayoutManager(context,
-                context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
+            context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
         mUserGridView.getRecyclerView().setLayoutManager(layoutManager);
         mUserGridView.buildAdapter();
         mUserGridView.setUserSelectionListener(this::onUserSelected);
 
-        mCarUserManagerHelper = new CarUserManagerHelper(context);
+        // Hide the user grid by default. It will only be made visible by clicking on a cancel
+        // button in a bouncer.
+        hide();
 
-        mShortAnimDuration = mContainer.getResources()
+        mShortAnimDuration = container.getResources()
             .getInteger(android.R.integer.config_shortAnimTime);
     }
 
+    /**
+     * Makes user grid visible.
+     */
     public void show() {
-        if (mCarUserManagerHelper.isHeadlessSystemUser()) {
-            showUserGrid();
-        }
-        if (mShowing) {
-            return;
-        }
-        mShowing = true;
-        mParent.setVisibility(View.VISIBLE);
-    }
-
-    public void hide() {
-        mShowing = false;
-        mParent.setVisibility(View.GONE);
-    }
-
-    public void onUserSwitched(int newUserId) {
-        toggleSwitchInProgress(false);
-        mParent.post(this::dismissKeyguard);
-    }
-
-    private void onUserSelected(UserGridRecyclerView.UserRecord record) {
-        if (mCarUserManagerHelper.isHeadlessSystemUser()) {
-            hideUserGrid();
-        }
-
-        if (record.mIsForeground || (record.mIsStartGuestSession
-                && mCarUserManagerHelper.isForegroundUserGuest())) {
-            dismissKeyguard();
-            return;
-        }
-        toggleSwitchInProgress(true);
-    }
-
-    private void showUserGrid() {
         mUserGridView.setVisibility(View.VISIBLE);
     }
 
-    private void hideUserGrid() {
+    /**
+     * Hides the user grid.
+     */
+    public void hide() {
         mUserGridView.setVisibility(View.INVISIBLE);
     }
 
-    // Dismisses the keyguard and shows bouncer if authentication is necessary.
-    private void dismissKeyguard() {
-        mStatusBar.executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
-                true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+    /**
+     * @return {@code true} if user grid is visible, {@code false} otherwise.
+     */
+    public boolean isVisible() {
+        return mUserGridView.getVisibility() == View.VISIBLE;
     }
 
-    private void toggleSwitchInProgress(boolean inProgress) {
-        if (inProgress) {
-            fadeOut(mContainer);
-        } else {
-            fadeIn(mContainer);
+    /**
+     * Every time user clicks on an item in the switcher, we hide the switcher, either
+     * gradually or immediately.
+     *
+     * We dismiss the entire keyguard if user clicked on the foreground user (user we're already
+     * logged in as).
+     */
+    private void onUserSelected(UserGridRecyclerView.UserRecord record) {
+        if (record.mIsForeground) {
+            hide();
+            mStatusBar.dismissKeyguard();
+            return;
         }
+        // Switching is about to happen, since it takes time, fade out the switcher gradually.
+        fadeOut();
     }
 
-    private void fadeOut(View view) {
-        view.animate()
+    private void fadeOut() {
+        mUserGridView.animate()
                 .alpha(0.0f)
                 .setDuration(mShortAnimDuration)
                 .setListener(new AnimatorListenerAdapter() {
                     @Override
                     public void onAnimationEnd(Animator animation) {
-                        view.setVisibility(View.GONE);
+                        hide();
+                        mUserGridView.setAlpha(1.0f);
                     }
                 });
-    }
 
-    private void fadeIn(View view) {
-        view.animate()
-                .alpha(1.0f)
-                .setDuration(mShortAnimDuration)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animator) {
-                        view.setAlpha(0.0f);
-                        view.setVisibility(View.VISIBLE);
-                    }
-                });
     }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
new file mode 100644
index 0000000..f204c42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package java.lang.annotation;
+
+@Retention(RetentionPolicy.SOURCE)
+public @interface ShadeViewRefactor {
+  /**
+   * Returns the refactor component.
+   * @return the refactor component.
+   */
+  RefactorComponent value();
+
+  public enum RefactorComponent {
+    ADAPTER,
+    LAYOUT_ALGORITHM,
+    STATE_RESOLVER,
+    DECORATOR,
+    INPUT,
+    COORDINATOR,
+    SHADE_VIEW
+  }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 1883aa7..52844fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -134,6 +134,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.ShadeViewRefactor;
+import java.lang.annotation.ShadeViewRefactor.RefactorComponent;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -465,18 +467,22 @@
     private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
     private NotificationPanelView mNotificationPanel;
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -524,6 +530,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onFinishInflate() {
         super.onFinishInflate();
 
@@ -534,6 +541,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onDensityOrFontScaleChanged() {
         inflateFooterView();
         inflateEmptyShadeView();
@@ -541,6 +549,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onThemeChanged() {
         int which;
         if (mStatusBarState == StatusBarState.KEYGUARD
@@ -557,6 +566,7 @@
     }
 
     @VisibleForTesting
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateFooter() {
         boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications();
         boolean showFooterView = (showDismissView ||
@@ -570,6 +580,7 @@
     /**
      * Return whether there are any clearable notifications
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean hasActiveClearableNotifications() {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -584,7 +595,8 @@
         return false;
     }
 
-    public RemoteInputController.Delegate createDelegate() {
+  @ShadeViewRefactor(RefactorComponent.INPUT)
+  public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
             public void setRemoteInputActive(NotificationData.Entry entry,
                     boolean remoteInputActive) {
@@ -605,6 +617,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         Dependency.get(StatusBarStateController.class).addListener(mStateListener);
@@ -612,6 +625,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
@@ -619,11 +633,13 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public NotificationSwipeActionHelper getSwipeActionHelper() {
         return mSwipeHelper;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void onMenuClicked(View view, int x, int y, MenuItem item) {
         if (mLongPressListener == null) {
             return;
@@ -637,6 +653,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void onMenuReset(View row) {
         if (mTranslatingParentView != null && row == mTranslatingParentView) {
             mMenuExposedView = null;
@@ -645,6 +662,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void onMenuShown(View row) {
         mMenuExposedView = mTranslatingParentView;
         if (row instanceof ExpandableNotificationRow) {
@@ -656,6 +674,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onUiModeChanged() {
         mBgColor = mContext.getColor(R.color.notification_shade_background_color);
         updateBackgroundDimming();
@@ -670,6 +689,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.DECORATOR)
     protected void onDraw(Canvas canvas) {
         if (mShouldDrawNotificationBackground
                 && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
@@ -686,6 +706,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.DECORATOR)
     private void drawBackground(Canvas canvas) {
         final int lockScreenLeft = mSidePaddings;
         final int lockScreenRight = getWidth() - mSidePaddings;
@@ -729,6 +750,7 @@
         updateClipping();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateBackgroundDimming() {
         // No need to update the background color if it's not being drawn.
         if (!mShouldDrawNotificationBackground) {
@@ -755,6 +777,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void initView(Context context) {
         mScroller = new OverScroller(getContext());
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
@@ -786,10 +809,12 @@
                 R.dimen.heads_up_status_bar_padding);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void notifyHeightChangeListener(ExpandableView view) {
         notifyHeightChangeListener(view, false /* needsAnimation */);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
         if (mOnHeightChangedListener != null) {
             mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
@@ -797,6 +822,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
@@ -816,6 +842,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         // we layout all our children centered on the top
         float centerX = getWidth() / 2.0f;
@@ -838,6 +865,7 @@
         updateAlgorithmLayoutMinHeight();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
         if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
             mNeedViewResizeAnimation = true;
@@ -845,18 +873,21 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
         mAmbientState.setSpeedBumpIndex(newIndex);
         mNoAmbient = noAmbient;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setChildLocationsChangedListener(
             NotificationLogger.OnChildLocationsChangedListener listener) {
         mListener = listener;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     public boolean isInVisibleLocation(ExpandableNotificationRow row) {
         ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
         if (childViewState == null) {
@@ -871,12 +902,14 @@
         return true;
     }
 
+    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     private void setMaxLayoutHeight(int maxLayoutHeight) {
         mMaxLayoutHeight = maxLayoutHeight;
         mShelf.setMaxLayoutHeight(maxLayoutHeight);
         updateAlgorithmHeightAndPadding();
     }
 
+    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     private void updateAlgorithmHeightAndPadding() {
         mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding,
                 mInterpolatedDarkAmount);
@@ -885,6 +918,7 @@
         mAmbientState.setTopPadding(mTopPadding);
     }
 
+    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     private void updateAlgorithmLayoutMinHeight() {
         mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
                 ? getLayoutMinHeight() : 0);
@@ -894,6 +928,7 @@
      * Updates the children views according to the stack scroll algorithm. Call this whenever
      * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
      */
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateChildren() {
         updateScrollStateForAddedChildren();
         mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
@@ -908,6 +943,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void onPreDrawDuringAnimation() {
         mShelf.updateAppearance();
         updateClippingToTopRoundedCorner();
@@ -916,6 +952,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateClippingToTopRoundedCorner() {
         Float clipStart = (float) mTopPadding
                 + mStackTranslation
@@ -938,6 +975,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateScrollStateForAddedChildren() {
         if (mChildrenToAddAnimated.isEmpty()) {
             return;
@@ -961,6 +999,7 @@
         clampScrollPosition();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateForcedScroll() {
         if (mForcedScroll != null && (!mForcedScroll.hasFocus()
                 || !mForcedScroll.isAttachedToWindow())) {
@@ -982,6 +1021,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void requestChildrenUpdate() {
         if (!mChildrenUpdateRequested) {
             getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
@@ -990,10 +1030,12 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private boolean isCurrentlyAnimating() {
         return mStateAnimator.isRunning();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void clampScrollPosition() {
         int scrollRange = getScrollRange();
         if (scrollRange < mOwnScrollY) {
@@ -1001,10 +1043,12 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public int getTopPadding() {
         return mTopPadding;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void setTopPadding(int topPadding, boolean animate) {
         if (mRegularTopPadding != topPadding) {
             mRegularTopPadding = topPadding;
@@ -1026,6 +1070,7 @@
      *
      * @param height the expanded height of the panel
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void setExpandedHeight(float height) {
         mExpandedHeight = height;
         setIsExpanded(height > 0);
@@ -1092,6 +1137,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void setRequestedClipBounds(Rect clipRect) {
         mRequestedClipBounds = clipRect;
         updateClipping();
@@ -1100,10 +1146,12 @@
     /**
      * Return the height of the content ignoring the footer.
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getIntrinsicContentHeight() {
         return mIntrinsicContentHeight;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void updateClipping() {
         boolean animatingClipping = mInterpolatedDarkAmount > 0 && mInterpolatedDarkAmount < 1;
         boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
@@ -1125,6 +1173,7 @@
      * @return The translation at the beginning when expanding.
      * Measured relative to the resting position.
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private float getExpandTranslationStart() {
         return -mTopPadding + getMinExpansionHeight();
     }
@@ -1133,6 +1182,7 @@
      * @return the position from where the appear transition starts when expanding.
      * Measured in absolute height.
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private float getAppearStartPosition() {
         if (isHeadsUpTransition()) {
             return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
@@ -1145,6 +1195,7 @@
      * intrinsic height, which also includes whether the notification is system expanded and
      * is mainly used when dragging down from a heads up notification.
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getTopHeadsUpPinnedHeight() {
         NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
         if (topEntry == null) {
@@ -1165,6 +1216,7 @@
      * @return the position from where the appear transition ends when expanding.
      * Measured in absolute height.
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private float getAppearEndPosition() {
         int appearPosition;
         int notGoneChildCount = getNotGoneChildCount();
@@ -1184,6 +1236,7 @@
         return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean isHeadsUpTransition() {
         return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
                 && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
@@ -1193,6 +1246,7 @@
      * @param height the height of the panel
      * @return the fraction of the appear animation that has been performed
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public float getAppearFraction(float height) {
         float appearEndPosition = getAppearEndPosition();
         float appearStartPosition = getAppearStartPosition();
@@ -1200,10 +1254,12 @@
                 / (appearEndPosition - appearStartPosition);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public float getStackTranslation() {
         return mStackTranslation;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void setStackTranslation(float stackTranslation) {
         if (stackTranslation != mStackTranslation) {
             mStackTranslation = stackTranslation;
@@ -1218,19 +1274,23 @@
      *
      * @return either the layout height or the externally defined height, whichever is smaller
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private int getLayoutHeight() {
         return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
     }
 
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public int getFirstItemMinHeight() {
         final ExpandableView firstChild = getFirstChildNotGone();
         return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
         mLongPressListener = listener;
     }
 
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public void setQsContainer(ViewGroup qsContainer) {
         mQsContainer = qsContainer;
     }
@@ -1240,6 +1300,7 @@
      * re-invoking dismiss logic in case the notification has not made its way out yet).
      */
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onChildDismissed(View view) {
         ExpandableNotificationRow row = (ExpandableNotificationRow) view;
         if (!row.isDismissed()) {
@@ -1257,6 +1318,8 @@
      *
      * @param view view (e.g. notification) to dismiss from the layout
      */
+
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void handleChildViewDismissed(View view) {
         if (mDismissAllInProgress) {
             return;
@@ -1296,6 +1359,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onChildSnappedBack(View animView, float targetLeft) {
         mAmbientState.onDragFinished(animView);
         updateContinuousShadowDrawing();
@@ -1316,12 +1380,14 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
         // Returning true prevents alpha fading.
         return !mFadeNotificationsOnDismiss;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void onBeginDrag(View v) {
         mFalsingManager.onNotificatonStartDismissing();
         setSwipingInProgress(true);
@@ -1334,6 +1400,7 @@
         requestChildrenUpdate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public static boolean isPinnedHeadsUp(View v) {
         if (v instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -1342,6 +1409,7 @@
         return false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     private boolean isHeadsUp(View v) {
         if (v instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -1351,17 +1419,20 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void onDragCancelled(View v) {
         mFalsingManager.onNotificatonStopDismissing();
         setSwipingInProgress(false);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public float getFalsingThresholdFactor() {
         return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public View getChildAtPosition(MotionEvent ev) {
         View child = getChildAtPosition(ev.getX(), ev.getY());
         if (child instanceof ExpandableNotificationRow) {
@@ -1382,6 +1453,7 @@
         return child;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
         getLocationOnScreen(mTempInt2);
         float localTouchY = touchY - mTempInt2[1];
@@ -1412,12 +1484,14 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
         getLocationOnScreen(mTempInt2);
         return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public ExpandableView getChildAtPosition(float touchX, float touchY) {
         return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
 
@@ -1431,6 +1505,7 @@
      * @param requireMinHeight Whether a minimum height is required for a child to be returned.
      * @return the child at the given location.
      */
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private ExpandableView getChildAtPosition(float touchX, float touchY,
             boolean requireMinHeight) {
         // find the view under the pointer, accounting for GONE views
@@ -1471,6 +1546,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public boolean canChildBeExpanded(View v) {
         return v instanceof ExpandableNotificationRow
                 && ((ExpandableNotificationRow) v).isExpandable()
@@ -1480,6 +1556,7 @@
 
     /* Only ever called as a consequence of an expansion gesture in the shade. */
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setUserExpandedChild(View v, boolean userExpanded) {
         if (v instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -1502,6 +1579,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setExpansionCancelled(View v) {
         if (v instanceof ExpandableNotificationRow) {
             ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
@@ -1509,6 +1587,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setUserLockedChild(View v, boolean userLocked) {
         if (v instanceof ExpandableNotificationRow) {
             ((ExpandableNotificationRow) v).setUserLocked(userLocked);
@@ -1518,6 +1597,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void expansionStateChanged(boolean isExpanding) {
         mExpandingNotification = isExpanding;
         if (!mExpandedInThisMotion) {
@@ -1527,14 +1607,17 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getMaxExpandHeight(ExpandableView view) {
         return view.getMaxContentHeight();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setScrollingEnabled(boolean enable) {
         mScrollingEnabled = enable;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void lockScrollTo(View v) {
         if (mForcedScroll == v) {
             return;
@@ -1543,6 +1626,7 @@
         scrollTo(v);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean scrollTo(View v) {
         ExpandableView expandableView = (ExpandableView) v;
         int positionInLinearLayout = getPositionInLinearLayout(v);
@@ -1564,6 +1648,7 @@
      * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
      * the IME.
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
         return positionInLinearLayout + v.getIntrinsicHeight() +
                 getImeInset() - getHeight()
@@ -1571,6 +1656,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         mBottomInset = insets.getSystemWindowInsetBottom();
 
@@ -1588,6 +1674,7 @@
         return insets;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private Runnable mReclamp = new Runnable() {
         @Override
         public void run() {
@@ -1599,28 +1686,34 @@
         }
     };
 
-    private void setExpandingEnabled(boolean enable) {
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    public void setExpandingEnabled(boolean enable) {
         mExpandHelper.setEnabled(enable);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean isScrollingEnabled() {
         return mScrollingEnabled;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public boolean canChildBeDismissed(View v) {
         return StackScrollAlgorithm.canChildBeDismissed(v);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean isAntiFalsingNeeded() {
         return onKeyguard();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean onKeyguard() {
         return mStatusBarState == StatusBarState.KEYGUARD;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void setSwipingInProgress(boolean isSwiped) {
         mSwipingInProgress = isSwiped;
         if (isSwiped) {
@@ -1629,6 +1722,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
@@ -1639,12 +1733,14 @@
         initView(getContext());
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
         mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
                 true /* isDismissAll */);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void snapViewIfNeeded(ExpandableNotificationRow child) {
         boolean animate = mIsExpanded || isPinnedHeadsUp(child);
         // If the child is showing the notification menu snap to that
@@ -1653,11 +1749,13 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
         return this;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onTouchEvent(MotionEvent ev) {
         boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
                 || ev.getActionMasked() == MotionEvent.ACTION_UP;
@@ -1706,6 +1804,7 @@
         return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void dispatchDownEventToScroller(MotionEvent ev) {
         MotionEvent downEvent = MotionEvent.obtain(ev);
         downEvent.setAction(MotionEvent.ACTION_DOWN);
@@ -1714,6 +1813,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onGenericMotionEvent(MotionEvent event) {
         if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
                 || mDisallowScrollingInThisMotion) {
@@ -1746,6 +1846,7 @@
         return super.onGenericMotionEvent(event);
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private boolean onScrollTouch(MotionEvent ev) {
         if (!isScrollingEnabled()) {
             return false;
@@ -1882,10 +1983,12 @@
         return true;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     protected boolean isInsideQsContainer(MotionEvent ev) {
         return ev.getY() < mQsContainer.getBottom();
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void onOverScrollFling(boolean open, int initialVelocity) {
         if (mOverscrollTopChangedListener != null) {
             mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
@@ -1901,6 +2004,7 @@
      * @return The amount of scrolling to be performed by the scroller,
      * not handled by the overScroll amount.
      */
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private float overScrollUp(int deltaY, int range) {
         deltaY = Math.max(deltaY, 0);
         float currentTopAmount = getCurrentOverScrollAmount(true);
@@ -1934,6 +2038,7 @@
      * @return The amount of scrolling to be performed by the scroller,
      * not handled by the overScroll amount.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private float overScrollDown(int deltaY) {
         deltaY = Math.min(deltaY, 0);
         float currentBottomAmount = getCurrentOverScrollAmount(false);
@@ -1958,6 +2063,7 @@
         return scrollAmount;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void onSecondaryPointerUp(MotionEvent ev) {
         final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
                 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
@@ -1975,12 +2081,14 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void initVelocityTrackerIfNotExists() {
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void recycleVelocityTracker() {
         if (mVelocityTracker != null) {
             mVelocityTracker.recycle();
@@ -1988,6 +2096,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void initOrResetVelocityTracker() {
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
@@ -1996,10 +2105,12 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setFinishScrollingCallback(Runnable runnable) {
         mFinishScrollingCallback = runnable;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void animateScroll() {
         if (mScroller.computeScrollOffset()) {
             int oldY = mOwnScrollY;
@@ -2030,6 +2141,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
             int maxOverScrollY) {
 
@@ -2061,6 +2173,7 @@
      * @param onTop     Should the effect be applied on top of the scroller.
      * @param animate   Should an animation be performed.
      */
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
         setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
     }
@@ -2073,6 +2186,8 @@
      * @param onTop   Should the effect be applied on top of the scroller.
      * @param animate Should an animation be performed.
      */
+
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
         setOverScrollAmount(amount, onTop, animate, true);
     }
@@ -2085,6 +2200,7 @@
      * @param animate         Should an animation be performed.
      * @param cancelAnimators Should running animations be cancelled.
      */
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
             boolean cancelAnimators) {
         setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
@@ -2100,6 +2216,7 @@
      * @param isRubberbanded  The value which will be passed to
      *                        {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
      */
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
             boolean cancelAnimators, boolean isRubberbanded) {
         if (cancelAnimators) {
@@ -2108,6 +2225,7 @@
         setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
             boolean isRubberbanded) {
         amount = Math.max(0, amount);
@@ -2123,6 +2241,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
         mExpandHelper.onlyObserveMovements(amount > 1.0f);
         if (mDontReportNextOverScroll) {
@@ -2134,19 +2253,23 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void setOverscrollTopChangedListener(
             OnOverscrollTopChangedListener overscrollTopChangedListener) {
         mOverscrollTopChangedListener = overscrollTopChangedListener;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public float getCurrentOverScrollAmount(boolean top) {
         return mAmbientState.getOverScrollAmount(top);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public float getCurrentOverScrolledPixels(boolean top) {
         return top ? mOverScrolledTopPixels : mOverScrolledBottomPixels;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void setOverScrolledPixels(float amount, boolean onTop) {
         if (onTop) {
             mOverScrolledTopPixels = amount;
@@ -2155,6 +2278,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void onCustomOverScrolled(int scrollY, boolean clampedY) {
         // Treat animating scrolls differently; see #computeScroll() for why.
         if (!mScroller.isFinished()) {
@@ -2174,6 +2298,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void springBack() {
         int scrollRange = getScrollRange();
         boolean overScrolledTop = mOwnScrollY <= 0;
@@ -2197,6 +2322,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getScrollRange() {
         // In current design, it only use the top HUN to treat all of HUNs
         // although there are more than one HUNs
@@ -2210,6 +2336,7 @@
         return scrollRange;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getImeInset() {
         return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
     }
@@ -2217,6 +2344,7 @@
     /**
      * @return the first child which has visibility unequal to GONE
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public ExpandableView getFirstChildNotGone() {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -2231,6 +2359,7 @@
     /**
      * @return the child before the given view which has visibility unequal to GONE
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public ExpandableView getViewBeforeView(ExpandableView view) {
         ExpandableView previousView = null;
         int childCount = getChildCount();
@@ -2250,6 +2379,7 @@
      * @return The first child which has visibility unequal to GONE which is currently below the
      * given translationY or equal to it.
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -2281,6 +2411,7 @@
     /**
      * @return the last child which has visibility unequal to GONE
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public View getLastChildNotGone() {
         int childCount = getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
@@ -2295,6 +2426,7 @@
     /**
      * @return the number of children which have visibility unequal to GONE
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getNotGoneChildCount() {
         int childCount = getChildCount();
         int count = 0;
@@ -2307,6 +2439,7 @@
         return count;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateContentHeight() {
         int height = 0;
         float previousPaddingRequest = mPaddingBetweenElements;
@@ -2380,15 +2513,18 @@
         mAmbientState.setLayoutMaxHeight(mContentHeight);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean isPulsing(NotificationData.Entry entry) {
         return mAmbientState.isPulsing(entry);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean hasPulsingNotifications() {
         return mPulsing;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateScrollability() {
         boolean scrollable = !mQsExpanded && getScrollRange() > 0;
         if (scrollable != mScrollable) {
@@ -2398,6 +2534,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateForwardAndBackwardScrollability() {
         boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
         boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
@@ -2410,6 +2547,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateBackground() {
         // No need to update the background color if it's not being drawn.
         if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
@@ -2437,6 +2575,7 @@
         mAnimateNextBackgroundTop = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void abortBackgroundAnimators() {
         if (mBottomAnimator != null) {
             mBottomAnimator.cancel();
@@ -2446,10 +2585,12 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private boolean areBoundsAnimating() {
         return mBottomAnimator != null || mTopAnimator != null;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void startBackgroundAnimation() {
         // left and right are always instantly applied
         mCurrentBounds.left = mBackgroundBounds.left;
@@ -2458,6 +2599,7 @@
         startTopAnimation();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void startTopAnimation() {
         int previousEndValue = mEndAnimationRect.top;
         int newEndValue = mBackgroundBounds.top;
@@ -2506,6 +2648,7 @@
         mTopAnimator = animator;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void startBottomAnimation() {
         int previousStartValue = mStartAnimationRect.bottom;
         int previousEndValue = mEndAnimationRect.bottom;
@@ -2554,11 +2697,13 @@
         mBottomAnimator = animator;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void setBackgroundTop(int top) {
         mCurrentBounds.top = top;
         invalidate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setBackgroundBottom(int bottom) {
         mCurrentBounds.bottom = bottom;
         invalidate();
@@ -2567,6 +2712,7 @@
     /**
      * Update the background bounds to the new desired bounds
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateBackgroundBounds() {
         getLocationInWindow(mTempInt2);
         mBackgroundBounds.left = mTempInt2[0] + mSidePaddings;
@@ -2628,6 +2774,7 @@
         mBackgroundBounds.bottom = Math.max(bottom, top);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private ActivatableNotificationView getFirstPinnedHeadsUp() {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -2643,6 +2790,7 @@
         return null;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private ActivatableNotificationView getLastChildWithBackground() {
         int childCount = getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
@@ -2655,6 +2803,7 @@
         return null;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private ActivatableNotificationView getFirstChildWithBackground() {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -2674,6 +2823,7 @@
      *                  numbers mean that the finger/cursor is moving down the screen,
      *                  which means we want to scroll towards the top.
      */
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     protected void fling(int velocityY) {
         if (getChildCount() > 0) {
             int scrollRange = getScrollRange();
@@ -2711,6 +2861,7 @@
      * @return Whether a fling performed on the top overscroll edge lead to the expanded
      * overScroll view (i.e QS).
      */
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private boolean shouldOverScrollFling(int initialVelocity) {
         float topOverScroll = getCurrentOverScrollAmount(true);
         return mScrolledToTopOnFirstDown
@@ -2728,6 +2879,7 @@
      * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
      *                               {@code qsHeight} is the final top padding
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void updateTopPadding(float qsHeight, boolean animate,
             boolean ignoreIntrinsicPadding) {
         int topPadding = (int) qsHeight;
@@ -2742,10 +2894,12 @@
         setExpandedHeight(mExpandedHeight);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void setMaxTopPadding(int maxTopPadding) {
         mMaxTopPadding = maxTopPadding;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getLayoutMinHeight() {
         if (isHeadsUpTransition()) {
             return getTopHeadsUpPinnedHeight();
@@ -2753,6 +2907,7 @@
         return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getFirstChildIntrinsicHeight() {
         final ExpandableView firstChild = getFirstChildNotGone();
         int firstChildMinHeight = firstChild != null
@@ -2766,10 +2921,13 @@
         return firstChildMinHeight;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public float getTopPaddingOverflow() {
         return mTopPaddingOverflow;
     }
 
+
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getPeekHeight() {
         final ExpandableView firstChild = getFirstChildNotGone();
         final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
@@ -2781,10 +2939,12 @@
         return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int clampPadding(int desiredPadding) {
         return Math.max(desiredPadding, mIntrinsicPadding);
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private float getRubberBandFactor(boolean onTop) {
         if (!onTop) {
             return RUBBER_BAND_FACTOR_NORMAL;
@@ -2804,11 +2964,13 @@
      * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
      * overscroll view (e.g. expand QS).
      */
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private boolean isRubberbanded(boolean onTop) {
         return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
                 || !mScrolledToTopOnFirstDown;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void endDrag() {
         setIsBeingDragged(false);
 
@@ -2822,12 +2984,14 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
         ev.offsetLocation(sourceView.getX(), sourceView.getY());
         ev.offsetLocation(-targetView.getX(), -targetView.getY());
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         initDownStates(ev);
         handleEmptySpaceClick(ev);
@@ -2863,6 +3027,7 @@
         return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void handleEmptySpaceClick(MotionEvent ev) {
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_MOVE:
@@ -2880,6 +3045,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void initDownStates(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mExpandedInThisMotion = false;
@@ -2892,10 +3058,12 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setChildTransferInProgress(boolean childTransferInProgress) {
         mChildTransferInProgress = childTransferInProgress;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     @Override
     public void onViewRemoved(View child) {
         super.onViewRemoved(child);
@@ -2906,6 +3074,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     @Override
     public void cleanUpViewState(View child) {
         if (child == mTranslatingParentView) {
@@ -2915,6 +3084,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
         if (disallowIntercept) {
@@ -2922,6 +3092,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void onViewRemovedInternal(View child, ViewGroup container) {
         if (mChangePositionInProgress) {
             // This is only a position change, don't do anything special
@@ -2946,6 +3117,7 @@
         focusNextViewIfFocused(child);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void focusNextViewIfFocused(View view) {
         if (view instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
@@ -2965,6 +3137,7 @@
 
     }
 
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     private boolean isChildInGroup(View child) {
         return child instanceof ExpandableNotificationRow
                 && mGroupManager.isChildInGroupWithSummary(
@@ -2977,6 +3150,7 @@
      * @param child The view to generate the remove animation for.
      * @return Whether an animation was generated.
      */
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private boolean generateRemoveAnimation(View child) {
         if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
             mAddedHeadsUpChildren.remove(child);
@@ -3002,6 +3176,7 @@
         return false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     private boolean isClickedHeadsUp(View child) {
         return HeadsUpUtil.isClickedHeadsUpNotification(child);
     }
@@ -3011,6 +3186,7 @@
      *
      * @return whether any child was removed from the list to animate
      */
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
         boolean hasAddEvent = false;
         for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
@@ -3035,6 +3211,7 @@
      * @return whether a view is not a top level child but a child notification and that group is
      * not expanded
      */
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     private boolean isChildInInvisibleGroup(View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
@@ -3052,6 +3229,7 @@
      *
      * @param removedChild the removed child
      */
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
         int startingPosition = getPositionInLinearLayout(removedChild);
         float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
@@ -3080,6 +3258,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getIntrinsicHeight(View view) {
         if (view instanceof ExpandableView) {
             ExpandableView expandableView = (ExpandableView) view;
@@ -3088,6 +3267,7 @@
         return view.getHeight();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getPositionInLinearLayout(View requestedView) {
         ExpandableNotificationRow childInGroup = null;
         ExpandableNotificationRow requestedRow = null;
@@ -3149,11 +3329,13 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onViewAdded(View child) {
         super.onViewAdded(child);
         onViewAddedInternal(child);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateFirstAndLastBackgroundViews() {
         ActivatableNotificationView firstChild = getFirstChildWithBackground();
         ActivatableNotificationView lastChild = getLastChildWithBackground();
@@ -3172,6 +3354,7 @@
         invalidate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void onViewAddedInternal(View child) {
         updateHideSensitiveForChild(child);
         ((ExpandableView) child).setOnHeightChangedListener(this);
@@ -3180,6 +3363,7 @@
         updateChronometerForChild(child);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void updateHideSensitiveForChild(View child) {
         if (child instanceof ExpandableView) {
             ExpandableView expandableView = (ExpandableView) child;
@@ -3188,15 +3372,18 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
         onViewRemovedInternal(row, childrenContainer);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void notifyGroupChildAdded(View row) {
         onViewAddedInternal(row);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setAnimationsEnabled(boolean animationsEnabled) {
         mAnimationsEnabled = animationsEnabled;
         updateNotificationAnimationStates();
@@ -3207,6 +3394,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateNotificationAnimationStates() {
         boolean running = mAnimationsEnabled || hasPulsingNotifications();
         mShelf.setAnimationsEnabled(running);
@@ -3218,18 +3406,21 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateAnimationState(View child) {
         updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
                 && (mIsExpanded || isPinnedHeadsUp(child)), child);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setExpandingNotification(ExpandableNotificationRow row) {
         mAmbientState.setExpandingNotification(row);
         requestChildrenUpdate();
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public void bindRow(ExpandableNotificationRow row) {
         row.setHeadsUpAnimatingAwayListener(animatingAway -> {
             mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
@@ -3238,11 +3429,13 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void applyExpandAnimationParams(ExpandAnimationParameters params) {
         mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
         requestChildrenUpdate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateAnimationState(boolean running, View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
@@ -3250,12 +3443,14 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public boolean isAddOrRemoveAnimationPending() {
         return mNeedsAnimation
                 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void generateAddAnimation(View child, boolean fromMoreCard) {
         if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
             // Generate Animations
@@ -3272,6 +3467,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void changeViewPosition(View child, int newIndex) {
         int currentIndex = indexOfChild(child);
 
@@ -3303,6 +3499,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void startAnimationToState() {
         if (mNeedsAnimation) {
             generateAllAnimationEvents();
@@ -3322,6 +3519,7 @@
         mGoToFullShadeDelay = 0;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateAllAnimationEvents() {
         generateHeadsUpAnimationEvents();
         generateChildRemovalEvents();
@@ -3341,6 +3539,7 @@
         generatePulsingAnimationEvent();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateHeadsUpAnimationEvents() {
         for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
             ExpandableNotificationRow row = eventPair.first;
@@ -3383,6 +3582,7 @@
         mAddedHeadsUpChildren.clear();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
         if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
             return false;
@@ -3390,6 +3590,7 @@
         return true;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateGroupExpansionEvent() {
         // Generate a group expansion/collapsing event if there is such a group at all
         if (mExpandedGroupView != null) {
@@ -3399,6 +3600,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateViewResizeEvent() {
         if (mNeedViewResizeAnimation) {
             boolean hasDisappearAnimation = false;
@@ -3419,6 +3621,7 @@
         mNeedViewResizeAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateSnapBackEvents() {
         for (View child : mSnappedBackChildren) {
             mAnimationEvents.add(new AnimationEvent(child,
@@ -3427,6 +3630,7 @@
         mSnappedBackChildren.clear();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateDragEvents() {
         for (View child : mDragAnimPendingChildren) {
             mAnimationEvents.add(new AnimationEvent(child,
@@ -3435,6 +3639,7 @@
         mDragAnimPendingChildren.clear();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateChildRemovalEvents() {
         for (View child : mChildrenToRemoveAnimated) {
             boolean childWasSwipedOut = mSwipedOutViews.contains(child);
@@ -3476,6 +3681,7 @@
         mChildrenToRemoveAnimated.clear();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generatePositionChangeEvents() {
         for (View child : mChildrenChangingPositions) {
             mAnimationEvents.add(new AnimationEvent(child,
@@ -3489,6 +3695,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateChildAdditionEvents() {
         for (View child : mChildrenToAddAnimated) {
             if (mFromMoreCardAdditions.contains(child)) {
@@ -3504,6 +3711,7 @@
         mFromMoreCardAdditions.clear();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateTopPaddingEvent() {
         if (mTopPaddingNeedsAnimation) {
             AnimationEvent event;
@@ -3520,6 +3728,7 @@
         mTopPaddingNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateActivateEvent() {
         if (mActivateNeedsAnimation) {
             mAnimationEvents.add(
@@ -3528,6 +3737,7 @@
         mActivateNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateAnimateEverythingEvent() {
         if (mEverythingNeedsAnimation) {
             mAnimationEvents.add(
@@ -3536,6 +3746,7 @@
         mEverythingNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateDimmedEvent() {
         if (mDimmedNeedsAnimation) {
             mAnimationEvents.add(
@@ -3544,6 +3755,7 @@
         mDimmedNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateHideSensitiveEvent() {
         if (mHideSensitiveNeedsAnimation) {
             mAnimationEvents.add(
@@ -3552,6 +3764,7 @@
         mHideSensitiveNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateDarkEvent() {
         if (mDarkNeedsAnimation) {
             AnimationEvent ev = new AnimationEvent(null,
@@ -3565,6 +3778,7 @@
         mDarkNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateGoToFullShadeEvent() {
         if (mGoToFullShadeNeedsAnimation) {
             mAnimationEvents.add(
@@ -3573,6 +3787,7 @@
         mGoToFullShadeNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private boolean onInterceptTouchEventScroll(MotionEvent ev) {
         if (!isScrollingEnabled()) {
             return false;
@@ -3682,6 +3897,7 @@
         return mIsBeingDragged;
     }
 
+    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
         return new StackScrollAlgorithm(context);
     }
@@ -3689,6 +3905,7 @@
     /**
      * @return Whether the specified motion event is actually happening over the content.
      */
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private boolean isInContentBounds(MotionEvent event) {
         return isInContentBounds(event.getY());
     }
@@ -3696,10 +3913,12 @@
     /**
      * @return Whether a y coordinate is inside the content.
      */
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean isInContentBounds(float y) {
         return y < getHeight() - getEmptyBottomMargin();
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private void setIsBeingDragged(boolean isDragged) {
         mIsBeingDragged = isDragged;
         if (isDragged) {
@@ -3709,6 +3928,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
         if (!hasWindowFocus) {
@@ -3717,6 +3937,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void clearChildFocus(View child) {
         super.clearChildFocus(child);
         if (mForcedScroll == child) {
@@ -3724,37 +3945,45 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void requestDisallowLongPress() {
         cancelLongPress();
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void requestDisallowDismiss() {
         mDisallowDismissInThisMotion = true;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void cancelLongPress() {
         mSwipeHelper.cancelLongPress();
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public boolean isScrolledToTop() {
         return mOwnScrollY == 0;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public boolean isScrolledToBottom() {
         return mOwnScrollY >= getScrollRange();
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public View getHostView() {
         return this;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getEmptyBottomMargin() {
         return Math.max(mMaxLayoutHeight - mContentHeight, 0);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void checkSnoozeLeavebehind() {
         if (mCheckForLeavebehind) {
             mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
@@ -3764,16 +3993,19 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void resetCheckSnoozeLeavebehind() {
         mCheckForLeavebehind = true;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onExpansionStarted() {
         mIsExpansionChanging = true;
         mAmbientState.setExpansionChanging(true);
         checkSnoozeLeavebehind();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onExpansionStopped() {
         mIsExpansionChanging = false;
         resetCheckSnoozeLeavebehind();
@@ -3786,6 +4018,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void clearUserLockedViews() {
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView child = (ExpandableView) getChildAt(i);
@@ -3796,6 +4029,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void clearTemporaryViews() {
         // lets make sure nothing is transient anymore
         clearTemporaryViewsInGroup(this);
@@ -3808,27 +4042,32 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
         while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
             viewGroup.removeTransientView(viewGroup.getTransientView(0));
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onPanelTrackingStarted() {
         mPanelTracking = true;
         mAmbientState.setPanelTracking(true);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onPanelTrackingStopped() {
         mPanelTracking = false;
         mAmbientState.setPanelTracking(false);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void resetScrollPosition() {
         mScroller.abortAnimation();
         setOwnScrollY(0);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void setIsExpanded(boolean isExpanded) {
         boolean changed = isExpanded != mIsExpanded;
         mIsExpanded = isExpanded;
@@ -3844,6 +4083,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void updateChronometers() {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -3851,6 +4091,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void updateChronometerForChild(View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
@@ -3859,6 +4100,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
         updateContentHeight();
         updateScrollPositionOnExpandInBottom(view);
@@ -3878,11 +4120,13 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onReset(ExpandableView view) {
         updateAnimationState(view);
         updateChronometerForChild(view);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
         if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
@@ -3907,15 +4151,18 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setOnHeightChangedListener(
             ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
         this.mOnHeightChangedListener = mOnHeightChangedListener;
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
         mOnEmptySpaceClickListener = listener;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onChildAnimationFinished() {
         setAnimationRunning(false);
         requestChildrenUpdate();
@@ -3924,6 +4171,7 @@
         clearHeadsUpDisappearRunning();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void clearHeadsUpDisappearRunning() {
         for (int i = 0; i < getChildCount(); i++) {
             View view = getChildAt(i);
@@ -3939,6 +4187,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void clearTransient() {
         for (ExpandableView view : mClearTransientViewsWhenFinished) {
             StackStateAnimator.removeTransientView(view);
@@ -3946,6 +4195,7 @@
         mClearTransientViewsWhenFinished.clear();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void runAnimationFinishedRunnables() {
         for (Runnable runnable : mAnimationFinishedRunnables) {
             runnable.run();
@@ -3956,6 +4206,7 @@
     /**
      * See {@link AmbientState#setDimmed}.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setDimmed(boolean dimmed, boolean animate) {
         dimmed &= onKeyguard();
         mAmbientState.setDimmed(dimmed);
@@ -3970,15 +4221,18 @@
     }
 
     @VisibleForTesting
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     boolean isDimmed() {
         return mAmbientState.isDimmed();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void setDimAmount(float dimAmount) {
         mDimAmount = dimAmount;
         updateBackgroundDimming();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void animateDimmed(boolean dimmed) {
         if (mDimAnimator != null) {
             mDimAnimator.cancel();
@@ -3994,8 +4248,8 @@
         mDimAnimator.addUpdateListener(mDimUpdateListener);
         mDimAnimator.start();
     }
-
-    private void setHideSensitive(boolean hideSensitive, boolean animate) {
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    public void setHideSensitive(boolean hideSensitive, boolean animate) {
         if (hideSensitive != mAmbientState.isHideSensitive()) {
             int childCount = getChildCount();
             for (int i = 0; i < childCount; i++) {
@@ -4015,6 +4269,7 @@
     /**
      * See {@link AmbientState#setActivatedChild}.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setActivatedChild(ActivatableNotificationView activatedChild) {
         mAmbientState.setActivatedChild(activatedChild);
         if (mAnimationsEnabled) {
@@ -4024,10 +4279,12 @@
         requestChildrenUpdate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public ActivatableNotificationView getActivatedChild() {
         return mAmbientState.getActivatedChild();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void applyCurrentState() {
         mCurrentStackScrollState.apply();
         if (mListener != null) {
@@ -4040,6 +4297,7 @@
         updateClippingToTopRoundedCorner();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateViewShadows() {
         // we need to work around an issue where the shadow would not cast between siblings when
         // their z difference is between 0 and 0.1
@@ -4082,6 +4340,7 @@
      *
      * @param lightTheme True if light theme should be used.
      */
+    @ShadeViewRefactor(RefactorComponent.DECORATOR)
     public void updateDecorViews(boolean lightTheme) {
         if (lightTheme == mUsingLightTheme) {
             return;
@@ -4094,6 +4353,7 @@
         mEmptyShadeView.setTextColor(textColor);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void goToFullShade(long delay) {
         mGoToFullShadeNeedsAnimation = true;
         mGoToFullShadeDelay = delay;
@@ -4101,15 +4361,18 @@
         requestChildrenUpdate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void cancelExpandHelper() {
         mExpandHelper.cancel();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void setIntrinsicPadding(int intrinsicPadding) {
         mIntrinsicPadding = intrinsicPadding;
         mAmbientState.setIntrinsicPadding(intrinsicPadding);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getIntrinsicPadding() {
         return mIntrinsicPadding;
     }
@@ -4117,11 +4380,13 @@
     /**
      * @return the y position of the first notification
      */
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public float getNotificationsTopY() {
         return mTopPadding + getStackTranslation();
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean shouldDelayChildPressedState() {
         return true;
     }
@@ -4129,6 +4394,7 @@
     /**
      * See {@link AmbientState#setDark}.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
         if (mAmbientState.isDark() == dark) {
             return;
@@ -4147,10 +4413,12 @@
         notifyHeightChangeListener(mShelf);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updatePanelTranslation() {
         setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setVerticalPanelTranslation(float verticalPanelTranslation) {
         mVerticalPanelTranslation = verticalPanelTranslation;
         updatePanelTranslation();
@@ -4161,11 +4429,13 @@
      * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
      * {@link #mAmbientState}'s dark mode is toggled.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateWillNotDraw() {
         boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
         setWillNotDraw(!willDraw);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void setDarkAmount(float darkAmount) {
         setDarkAmount(darkAmount, darkAmount);
     }
@@ -4179,6 +4449,7 @@
      * @param interpolatedDarkAmount The dark amount that follows the actual interpolation of the
      *                               animation curve.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
         mLinearDarkAmount = linearDarkAmount;
         mInterpolatedDarkAmount = interpolatedDarkAmount;
@@ -4201,6 +4472,7 @@
         requestChildrenUpdate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void notifyDarkAnimationStart(boolean dark) {
         // We only swap the scaling factor if we're fully dark or fully awake to avoid
         // interpolation issues when playing with the power button.
@@ -4212,6 +4484,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public long getDarkAnimationDuration(boolean dark) {
         long duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
         // Longer animation when sleeping with more than 1 notification
@@ -4221,6 +4494,7 @@
         return duration;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
         if (screenLocation == null || screenLocation.y < mTopPadding) {
             return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
@@ -4236,6 +4510,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private int getNotGoneIndex(View child) {
         int count = getChildCount();
         int notGoneIndex = 0;
@@ -4251,6 +4526,7 @@
         return -1;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setFooterView(@NonNull FooterView footerView) {
         int index = -1;
         if (mFooterView != null) {
@@ -4261,6 +4537,7 @@
         addView(mFooterView, index);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
         int index = -1;
         if (mEmptyShadeView != null) {
@@ -4271,6 +4548,7 @@
         addView(mEmptyShadeView, index);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateEmptyShadeView(boolean visible) {
         mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
 
@@ -4282,6 +4560,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateFooterView(boolean visible, boolean showDismissView) {
         if (mFooterView == null) {
             return;
@@ -4291,12 +4570,14 @@
         mFooterView.setSecondaryVisible(showDismissView, animate);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setDismissAllInProgress(boolean dismissAllInProgress) {
         mDismissAllInProgress = dismissAllInProgress;
         mAmbientState.setDismissAllInProgress(dismissAllInProgress);
         handleDismissAllClipping();
     }
 
+    @ShadeViewRefactor(RefactorComponent.ADAPTER)
     private void handleDismissAllClipping() {
         final int count = getChildCount();
         boolean previousChildWillBeDismissed = false;
@@ -4314,24 +4595,29 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean isFooterViewNotGone() {
         return mFooterView != null
                 && mFooterView.getVisibility() != View.GONE
                 && !mFooterView.willBeGone();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean isFooterViewContentVisible() {
         return mFooterView != null && mFooterView.isContentVisible();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public int getFooterViewHeight() {
         return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public int getEmptyShadeViewHeight() {
         return mEmptyShadeView.getHeight();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public float getBottomMostNotificationBottom() {
         final int count = getChildCount();
         float max = 0;
@@ -4349,15 +4635,18 @@
         return max + getStackTranslation();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setStatusBar(StatusBar statusBar) {
         this.mStatusBar = statusBar;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setGroupManager(NotificationGroupManager groupManager) {
         this.mGroupManager = groupManager;
         mGroupManager.setOnGroupChangeListener(this);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void requestAnimateEverything() {
         if (mIsExpanded && mAnimationsEnabled) {
             mEverythingNeedsAnimation = true;
@@ -4366,6 +4655,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public boolean isBelowLastNotification(float touchX, float touchY) {
         int childCount = getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
@@ -4397,6 +4687,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
         boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
                 && (mIsExpanded || changedRow.isPinned());
@@ -4417,12 +4708,14 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
         mStatusBar.requestNotificationUpdate();
     }
 
     /** @hide */
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
         event.setScrollable(mScrollable);
@@ -4433,6 +4726,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
         if (mScrollable) {
@@ -4453,6 +4747,7 @@
 
     /** @hide */
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
         if (super.performAccessibilityActionInternal(action, arguments)) {
             return true;
@@ -4485,10 +4780,12 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onGroupsChanged() {
         mStatusBar.requestNotificationUpdate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void generateChildOrderChangedEvent() {
         if (mIsExpanded && mAnimationsEnabled) {
             mGenerateChildOrderChangedEvent = true;
@@ -4498,29 +4795,35 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public int getContainerChildCount() {
         return getChildCount();
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public View getContainerChildAt(int i) {
         return getChildAt(i);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void removeContainerView(View v) {
         removeView(v);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void addContainerView(View v) {
         addView(v);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void runAfterAnimationFinished(Runnable runnable) {
         mAnimationFinishedRunnables.add(runnable);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mAmbientState.setHeadsUpManager(headsUpManager);
@@ -4528,6 +4831,7 @@
         mHeadsUpManager.setAnimationStateHandler(this);
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
         if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
             mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
@@ -4539,6 +4843,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setShadeExpanded(boolean shadeExpanded) {
         mAmbientState.setShadeExpanded(shadeExpanded);
         mStateAnimator.setShadeExpanded(shadeExpanded);
@@ -4551,31 +4856,37 @@
      * @param height          the height of the screen
      * @param bottomBarHeight the height of the bar on the bottom
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
         mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
         mStateAnimator.setHeadsUpAppearHeightBottom(height);
         requestChildrenUpdate();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setTrackingHeadsUp(ExpandableNotificationRow row) {
         mTrackingHeadsUp = row != null;
         mRoundnessManager.setTrackingHeadsUp(row);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setScrimController(ScrimController scrimController) {
         mScrimController = scrimController;
         mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void forceNoOverlappingRendering(boolean force) {
         mForceNoOverlappingRendering = force;
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean hasOverlappingRendering() {
         return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setAnimationRunning(boolean animationRunning) {
         if (animationRunning != mAnimationRunning) {
             if (animationRunning) {
@@ -4588,10 +4899,12 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean isExpanded() {
         return mIsExpanded;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setPulsing(boolean pulsing, boolean animated) {
         if (!mPulsing && !pulsing) {
             return;
@@ -4607,6 +4920,7 @@
         mNeedsAnimation |= animated;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void generatePulsingAnimationEvent() {
         if (mNeedingPulseAnimation != null) {
             int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
@@ -4616,16 +4930,19 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setQsExpanded(boolean qsExpanded) {
         mQsExpanded = qsExpanded;
         updateAlgorithmLayoutMinHeight();
         updateScrollability();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setQsExpansionFraction(float qsExpansionFraction) {
         mQsExpansionFraction = qsExpansionFraction;
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void setOwnScrollY(int ownScrollY) {
         if (ownScrollY != mOwnScrollY) {
             // We still want to call the normal scrolled changed for accessibility reasons
@@ -4636,6 +4953,7 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setShelf(NotificationShelf shelf) {
         int index = -1;
         if (mShelf != null) {
@@ -4649,10 +4967,12 @@
         shelf.bind(mAmbientState, this);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationShelf getNotificationShelf() {
         return mShelf;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
         if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
             mMaxDisplayedNotifications = maxDisplayedNotifications;
@@ -4661,25 +4981,30 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
         mShouldShowShelfOnly = shouldShowShelfOnly;
         updateAlgorithmLayoutMinHeight();
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getMinExpansionHeight() {
         return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
         mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
         updateClipping();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
         mHeadsUpAnimatingAway = headsUpAnimatingAway;
         updateClipping();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void setStatusBarState(int statusBarState) {
         mStatusBarState = statusBarState;
         mAmbientState.setStatusBarState(statusBarState);
@@ -4703,10 +5028,12 @@
         onUpdateRowStates();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setExpandingVelocity(float expandingVelocity) {
         mAmbientState.setExpandingVelocity(expandingVelocity);
     }
 
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public float getOpeningHeight() {
         if (mEmptyShadeView.getVisibility() == GONE) {
             return getMinExpansionHeight();
@@ -4715,29 +5042,34 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setIsFullWidth(boolean isFullWidth) {
         mAmbientState.setPanelFullWidth(isFullWidth);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setUnlockHintRunning(boolean running) {
         mAmbientState.setUnlockHintRunning(running);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setQsCustomizerShowing(boolean isShowing) {
         mAmbientState.setQsCustomizerShowing(isShowing);
         requestChildrenUpdate();
     }
 
-    @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
         mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
         mAntiBurnInOffsetX = antiBurnInOffsetX;
         updatePanelTranslation();
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
                         + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
@@ -4755,6 +5087,7 @@
                 mQsExpansionFraction));
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public boolean isFullyDark() {
         return mAmbientState.isFullyDark();
     }
@@ -4765,6 +5098,7 @@
      *
      * @param listener the listener to notify.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
         mExpandedHeightListeners.add(listener);
     }
@@ -4772,24 +5106,29 @@
     /**
      * Stop a listener from listening to the expandedHeight.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
         mExpandedHeightListeners.remove(listener);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setHeadsUpAppearanceController(
             HeadsUpAppearanceController headsUpAppearanceController) {
         mHeadsUpAppearanceController = headsUpAppearanceController;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setIconAreaController(NotificationIconAreaController controller) {
         mIconAreaController = controller;
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void manageNotifications(View v) {
         Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
         mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void clearAllNotifications() {
         // animate-swipe all dismissable notifications, then animate the shade closed
         int numChildren = getChildCount();
@@ -4852,6 +5191,7 @@
         performDismissAllAnimations(viewsToHide);
     }
 
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
         Runnable animationFinishAction = () -> {
             mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
@@ -4884,6 +5224,7 @@
     }
 
     @VisibleForTesting
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void inflateFooterView() {
         FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
                 R.layout.status_bar_notification_footer, this, false);
@@ -4895,7 +5236,8 @@
         setFooterView(footerView);
     }
 
-    private void inflateEmptyShadeView() {
+  @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+  private void inflateEmptyShadeView() {
         EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
                 R.layout.status_bar_no_notifications, this, false);
         view.setText(R.string.empty_shade_text);
@@ -4905,6 +5247,7 @@
     /**
      * Updates expanded, dimmed and locked states of notification rows.
      */
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onUpdateRowStates() {
         changeViewPosition(mFooterView, -1);
 
@@ -4925,13 +5268,15 @@
         mScrimController.setNotificationCount(getNotGoneChildCount());
     }
 
-    public void setNotificationPanel(NotificationPanelView notificationPanelView) {
+  @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+  public void setNotificationPanel(NotificationPanelView notificationPanelView) {
         mNotificationPanel = notificationPanelView;
     }
 
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public interface OnEmptySpaceClickListener {
         void onEmptySpaceClicked(float x, float y);
     }
@@ -4939,6 +5284,7 @@
     /**
      * A listener that gets notified when the overscroll at the top has changed.
      */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public interface OnOverscrollTopChangedListener {
 
         /**
@@ -4962,7 +5308,8 @@
         void flingTopOverscroll(float velocity, boolean open);
     }
 
-    private class NotificationSwipeHelper extends SwipeHelper
+  @ShadeViewRefactor(RefactorComponent.INPUT)
+  private class NotificationSwipeHelper extends SwipeHelper
             implements NotificationSwipeActionHelper {
         private static final long COVER_MENU_DELAY = 4000;
         private Runnable mFalsingCheck;
@@ -5158,7 +5505,8 @@
         }
     }
 
-    public boolean hasActiveNotifications() {
+  @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+  public boolean hasActiveNotifications() {
         return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
     }
 
@@ -5167,6 +5515,7 @@
 
     /* Only ever called as a consequence of a lockscreen expansion gesture. */
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onDraggedDown(View startingChild, int dragLengthY) {
         if (mStatusBarState == StatusBarState.KEYGUARD
                 && hasActiveNotifications() && (!mStatusBar.isDozing() || mStatusBar.isPulsing())) {
@@ -5189,6 +5538,7 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void onDragDownReset() {
         setDimmed(true /* dimmed */, true /* animated */);
         resetScrollPosition();
@@ -5196,27 +5546,32 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void onCrossedThreshold(boolean above) {
         setDimmed(!above /* dimmed */, true /* animate */);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void onTouchSlopExceeded() {
         cancelLongPress();
         checkSnoozeLeavebehind();
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void setEmptyDragAmount(float amount) {
         mNotificationPanel.setEmptyDragAmount(amount);
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean isFalsingCheckNeeded() {
         return mStatusBarState == StatusBarState.KEYGUARD;
     }
 
-    public void updateSpeedBumpIndex() {
+  @ShadeViewRefactor(RefactorComponent.INPUT)
+  public void updateSpeedBumpIndex() {
         int speedBumpIndex = 0;
         int currentIndex = 0;
         final int N = getChildCount();
@@ -5236,6 +5591,7 @@
         updateSpeedBumpIndex(speedBumpIndex, noAmbient);
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     private boolean isTouchInView(MotionEvent ev, View view) {
         if (view == null) {
             return false;
@@ -5253,6 +5609,7 @@
         return ret;
     }
 
+    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateContinuousShadowDrawing() {
         boolean continuousShadowUpdate = mAnimationRunning
                 || !mAmbientState.getDraggedViews().isEmpty();
@@ -5267,15 +5624,19 @@
     }
 
     @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void resetExposedMenuView(boolean animate, boolean force) {
         mSwipeHelper.resetExposedMenuView(animate, force);
     }
 
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
     public void closeControlsIfOutsideTouch(MotionEvent ev) {
         mSwipeHelper.closeControlsIfOutsideTouch(ev);
     }
 
-    static class AnimationEvent {
+  @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+  static class AnimationEvent {
 
         static AnimationFilter[] FILTERS = new AnimationFilter[]{
 
@@ -5575,7 +5936,8 @@
         }
     }
 
-    private final StateListener mStateListener = new StateListener() {
+  @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+  private final StateListener mStateListener = new StateListener() {
         @Override
         public void onStatePreChange(int oldState, int newState) {
             if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 426a0c15..fd32b5a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5296,7 +5296,9 @@
         try {
             INotificationManager notificationManager = mInjector.getNotificationManager();
             try {
-                notificationManager.enqueueNotificationWithTag(packageName, packageName,
+                // The calling uid must match either the package or op package, so use an op
+                // package that matches the cleared calling identity.
+                notificationManager.enqueueNotificationWithTag(packageName, "android",
                         id.mTag, id.mId, notification, userId);
             } catch (RemoteException e) {
                 /* ignore - local call */
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ce71dd2..03b7652 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -35,6 +35,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -203,6 +205,7 @@
 import com.android.server.lights.LightsManager;
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
 import com.android.server.notification.ManagedServices.UserProfiles;
+import com.android.server.pm.PackageManagerService;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -470,8 +473,8 @@
                 // Gather all notification listener components for candidate pkgs.
                 Set<ComponentName> approvedListeners =
                         mListeners.queryPackageForServices(whitelisted,
-                                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+                                MATCH_DIRECT_BOOT_AWARE
+                                        | MATCH_DIRECT_BOOT_UNAWARE, userId);
                 for (ComponentName cn : approvedListeners) {
                     try {
                         getBinderService().setNotificationListenerAccessGrantedForUser(cn,
@@ -507,8 +510,8 @@
             // only be one
             Set<ComponentName> approvedAssistants =
                     mAssistants.queryPackageForServices(defaultAssistantAccess,
-                            PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+                            MATCH_DIRECT_BOOT_AWARE
+                                    | MATCH_DIRECT_BOOT_UNAWARE, userId);
             for (ComponentName cn : approvedAssistants) {
                 try {
                     getBinderService().setNotificationAssistantAccessGrantedForUser(
@@ -1377,7 +1380,7 @@
             NotificationUsageStats usageStats, AtomicFile policyFile,
             ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
             UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm,
-            IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal) {
+            IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps) {
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1390,7 +1393,7 @@
         mUgmInternal = ugmInternal;
         mPackageManager = packageManager;
         mPackageManagerClient = packageManagerClient;
-        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
+        mAppOps = appOps;
         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
         mAppUsageStats = appUsageStats;
         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -1544,7 +1547,8 @@
                 LocalServices.getService(UsageStatsManagerInternal.class),
                 LocalServices.getService(DevicePolicyManagerInternal.class),
                 UriGrantsManager.getService(),
-                LocalServices.getService(UriGrantsManagerInternal.class));
+                LocalServices.getService(UriGrantsManagerInternal.class),
+                (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE));
 
         // register for various Intents
         IntentFilter filter = new IntentFilter();
@@ -2234,6 +2238,60 @@
         }
 
         @Override
+        public void setNotificationDelegate(String callingPkg, String delegate) {
+            checkCallerIsSameApp(callingPkg);
+            final int callingUid = Binder.getCallingUid();
+            UserHandle user = UserHandle.getUserHandleForUid(callingUid);
+            try {
+                ApplicationInfo info =
+                        mPackageManager.getApplicationInfo(delegate,
+                                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                                user.getIdentifier());
+                if (info != null) {
+                    mPreferencesHelper.setNotificationDelegate(
+                            callingPkg, callingUid, delegate, info.uid);
+                    savePolicyFile();
+                }
+            } catch (RemoteException e) {
+                // :(
+            }
+        }
+
+        @Override
+        public void revokeNotificationDelegate(String callingPkg) {
+            checkCallerIsSameApp(callingPkg);
+            mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid());
+            savePolicyFile();
+        }
+
+        @Override
+        public String getNotificationDelegate(String callingPkg) {
+            // callable by Settings also
+            checkCallerIsSystemOrSameApp(callingPkg);
+            return mPreferencesHelper.getNotificationDelegate(callingPkg, Binder.getCallingUid());
+        }
+
+        @Override
+        public boolean canNotifyAsPackage(String callingPkg, String targetPkg) {
+            checkCallerIsSameApp(callingPkg);
+            final int callingUid = Binder.getCallingUid();
+            UserHandle user = UserHandle.getUserHandleForUid(callingUid);
+            try {
+                ApplicationInfo info =
+                        mPackageManager.getApplicationInfo(targetPkg,
+                                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                                user.getIdentifier());
+                if (info != null) {
+                    return mPreferencesHelper.isDelegateAllowed(
+                            targetPkg, info.uid, callingPkg, callingUid);
+                }
+            } catch (RemoteException e) {
+                // :(
+            }
+            return false;
+        }
+
+        @Override
         public void updateNotificationChannelGroupForPackage(String pkg, int uid,
                 NotificationChannelGroup group) throws RemoteException {
             enforceSystemOrSystemUI("Caller not system or systemui");
@@ -4053,20 +4111,21 @@
             Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
                     + " notification=" + notification);
         }
-        checkCallerIsSystemOrSameApp(pkg);
-        checkRestrictedCategories(notification);
-
-        final int userId = ActivityManager.handleIncomingUser(callingPid,
-                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
-        final UserHandle user = new UserHandle(userId);
 
         if (pkg == null || notification == null) {
             throw new IllegalArgumentException("null not allowed: pkg=" + pkg
                     + " id=" + id + " notification=" + notification);
         }
 
-        // The system can post notifications for any package, let us resolve that.
-        final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
+        final int userId = ActivityManager.handleIncomingUser(callingPid,
+                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
+        final UserHandle user = UserHandle.of(userId);
+
+        // Can throw a SecurityException if the calling uid doesn't have permission to post
+        // as "pkg"
+        final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+
+        checkRestrictedCategories(notification);
 
         // Fix the notification as best we can.
         try {
@@ -4193,17 +4252,28 @@
         }
     }
 
-    private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
-        // The system can post notifications on behalf of any package it wants
-        if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
-            try {
-                return getContext().getPackageManager()
-                        .getPackageUidAsUser(opPackageName, userId);
-            } catch (NameNotFoundException e) {
-                /* ignore */
-            }
+    @VisibleForTesting
+    int resolveNotificationUid(String callingPkg, String targetPkg,
+            int callingUid, int userId) {
+        // posted from app A on behalf of app A
+        if (isCallerSameApp(targetPkg, callingUid) && TextUtils.equals(callingPkg, targetPkg)) {
+            return callingUid;
         }
-        return callingUid;
+
+        int targetUid = -1;
+        try {
+            targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
+        } catch (NameNotFoundException e) {
+            /* ignore */
+        }
+        // posted from app A on behalf of app B
+        if (targetUid != -1 && (isCallerAndroid(callingPkg, callingUid)
+                || mPreferencesHelper.isDelegateAllowed(
+                        targetPkg, targetUid, callingPkg, callingUid))) {
+            return targetUid;
+        }
+
+        throw new SecurityException("Caller " + callingUid + " cannot post for pkg " + targetPkg);
     }
 
     /**
@@ -4222,7 +4292,8 @@
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
             synchronized (mNotificationLock) {
-                if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
+                if (mNotificationsByKey.get(r.sbn.getKey()) == null
+                        && isCallerInstantApp(pkg, callingUid)) {
                     // Ephemeral apps have some special constraints for notifications.
                     // They are not allowed to create new notifications however they are allowed to
                     // update notifications created by the system (e.g. a foreground service
@@ -5149,11 +5220,11 @@
                     try {
                         Thread.sleep(waitMs);
                     } catch (InterruptedException e) { }
-                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
                             effect, "Notification (delayed)", record.getAudioAttributes());
                 }).start();
             } else {
-                mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+                mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
                         effect, "Notification", record.getAudioAttributes());
             }
             return true;
@@ -6282,6 +6353,11 @@
         checkCallerIsSameApp(pkg);
     }
 
+    private boolean isCallerAndroid(String callingPkg, int uid) {
+        return isUidSystemOrPhone(uid) && callingPkg != null
+                && PackageManagerService.PLATFORM_PACKAGE_NAME.equals(callingPkg);
+    }
+
     /**
      * Check if the notification is of a category type that is restricted to system use only,
      * if so throw SecurityException
@@ -6302,13 +6378,13 @@
         }
     }
 
-    private boolean isCallerInstantApp(String pkg) {
+    private boolean isCallerInstantApp(String pkg, int callingUid) {
         // System is always allowed to act for ephemeral apps.
-        if (isCallerSystemOrPhone()) {
+        if (isUidSystemOrPhone(callingUid)) {
             return false;
         }
 
-        mAppOps.checkPackage(Binder.getCallingUid(), pkg);
+        mAppOps.checkPackage(callingUid, pkg);
 
         try {
             ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
@@ -6324,7 +6400,10 @@
     }
 
     private void checkCallerIsSameApp(String pkg) {
-        final int uid = Binder.getCallingUid();
+        checkCallerIsSameApp(pkg, Binder.getCallingUid());
+    }
+
+    private void checkCallerIsSameApp(String pkg, int uid) {
         try {
             ApplicationInfo ai = mPackageManager.getApplicationInfo(
                     pkg, 0, UserHandle.getCallingUserId());
@@ -6340,6 +6419,24 @@
         }
     }
 
+    private boolean isCallerSameApp(String pkg) {
+        try {
+            checkCallerIsSameApp(pkg);
+            return true;
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    private boolean isCallerSameApp(String pkg, int uid) {
+        try {
+            checkCallerIsSameApp(pkg, uid);
+            return true;
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
     private static String callStateToString(int state) {
         switch (state) {
             case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 432d17c..593e7cd 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -66,12 +67,14 @@
 public class PreferencesHelper implements RankingConfig {
     private static final String TAG = "NotificationPrefHelper";
     private static final int XML_VERSION = 1;
+    private static final int UNKNOWN_UID = UserHandle.USER_NULL;
 
     @VisibleForTesting
     static final String TAG_RANKING = "ranking";
     private static final String TAG_PACKAGE = "package";
     private static final String TAG_CHANNEL = "channel";
     private static final String TAG_GROUP = "channelGroup";
+    private static final String TAG_DELEGATE = "delegate";
 
     private static final String ATT_VERSION = "version";
     private static final String ATT_NAME = "name";
@@ -82,6 +85,8 @@
     private static final String ATT_IMPORTANCE = "importance";
     private static final String ATT_SHOW_BADGE = "show_badge";
     private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
+    private static final String ATT_ENABLED = "enabled";
+    private static final String ATT_USER_ALLOWED = "allowed";
 
     private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
     private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
@@ -147,8 +152,7 @@
             }
             if (type == XmlPullParser.START_TAG) {
                 if (TAG_PACKAGE.equals(tag)) {
-                    int uid = XmlUtils.readIntAttribute(parser, ATT_UID,
-                            PackagePreferences.UNKNOWN_UID);
+                    int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
                     String name = parser.getAttributeValue(null, ATT_NAME);
                     if (!TextUtils.isEmpty(name)) {
                         if (forRestore) {
@@ -217,6 +221,24 @@
                                     r.channels.put(id, channel);
                                 }
                             }
+                            // Delegate
+                            if (TAG_DELEGATE.equals(tagName)) {
+                                int delegateId =
+                                        XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
+                                String delegateName =
+                                        XmlUtils.readStringAttribute(parser, ATT_NAME);
+                                boolean delegateEnabled = XmlUtils.readBooleanAttribute(
+                                        parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
+                                boolean userAllowed = XmlUtils.readBooleanAttribute(
+                                        parser, ATT_USER_ALLOWED, Delegate.DEFAULT_USER_ALLOWED);
+                                Delegate d = null;
+                                if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(delegateName)) {
+                                    d = new Delegate(
+                                            delegateName, delegateId, delegateEnabled, userAllowed);
+                                }
+                                r.delegate = d;
+                            }
+
                         }
 
                         try {
@@ -248,7 +270,7 @@
         final String key = packagePreferencesKey(pkg, uid);
         synchronized (mPackagePreferencess) {
             PackagePreferences
-                    r = (uid == PackagePreferences.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
+                    r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
                     : mPackagePreferencess.get(key);
             if (r == null) {
                 r = new PackagePreferences();
@@ -265,7 +287,7 @@
                     Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
                 }
 
-                if (r.uid == PackagePreferences.UNKNOWN_UID) {
+                if (r.uid == UNKNOWN_UID) {
                     mRestoredWithoutUids.put(pkg, r);
                 } else {
                     mPackagePreferencess.put(key, r);
@@ -357,7 +379,8 @@
                                 || r.showBadge != DEFAULT_SHOW_BADGE
                                 || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
                                 || r.channels.size() > 0
-                                || r.groups.size() > 0;
+                                || r.groups.size() > 0
+                                || r.delegate != null;
                 if (hasNonDefaultSettings) {
                     out.startTag(null, TAG_PACKAGE);
                     out.attribute(null, ATT_NAME, r.pkg);
@@ -378,6 +401,21 @@
                         out.attribute(null, ATT_UID, Integer.toString(r.uid));
                     }
 
+                    if (r.delegate != null) {
+                        out.startTag(null, TAG_DELEGATE);
+
+                        out.attribute(null, ATT_NAME, r.delegate.mPkg);
+                        out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
+                        if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
+                            out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
+                        }
+                        if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
+                            out.attribute(null, ATT_USER_ALLOWED,
+                                    Boolean.toString(r.delegate.mUserAllowed));
+                        }
+                        out.endTag(null, TAG_DELEGATE);
+                    }
+
                     for (NotificationChannelGroup group : r.groups.values()) {
                         group.writeXml(out);
                     }
@@ -923,16 +961,76 @@
      * considered for sentiment adjustments (and thus never show a blocking helper).
      */
     public void setAppImportanceLocked(String packageName, int uid) {
-        PackagePreferences PackagePreferences = getOrCreatePackagePreferences(packageName, uid);
-        if ((PackagePreferences.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
+        PackagePreferences prefs = getOrCreatePackagePreferences(packageName, uid);
+        if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
             return;
         }
 
-        PackagePreferences.lockedAppFields =
-                PackagePreferences.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
+        prefs.lockedAppFields = prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
         updateConfig();
     }
 
+    /**
+     * Returns the delegate for a given package, if it's allowed by the package and the user.
+     */
+    public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
+        PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+
+        if (prefs == null || prefs.delegate == null) {
+            return null;
+        }
+        if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
+            return null;
+        }
+        return prefs.delegate.mPkg;
+    }
+
+    /**
+     * Used by an app to delegate notification posting privileges to another apps.
+     */
+    public void setNotificationDelegate(String sourcePkg, int sourceUid,
+            String delegatePkg, int delegateUid) {
+        PackagePreferences prefs = getOrCreatePackagePreferences(sourcePkg, sourceUid);
+
+        boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
+        Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
+        prefs.delegate = delegate;
+        updateConfig();
+    }
+
+    /**
+     * Used by an app to turn off its notification delegate.
+     */
+    public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
+        PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+        if (prefs != null && prefs.delegate != null) {
+            prefs.delegate.mEnabled = false;
+            updateConfig();
+        }
+    }
+
+    /**
+     * Toggles whether an app can have a notification delegate on behalf of a user.
+     */
+    public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
+        PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+        if (prefs != null && prefs.delegate != null) {
+            prefs.delegate.mUserAllowed = userAllowed;
+            updateConfig();
+        }
+    }
+
+    /**
+     * Returns whether the given app is allowed on post notifications on behalf of the other given
+     * app.
+     */
+    public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
+            String potentialDelegatePkg, int potentialDelegateUid) {
+        PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+
+        return prefs != null && prefs.isValidDelegate(potentialDelegatePkg, potentialDelegateUid);
+    }
+
     @VisibleForTesting
     void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
         if (original.canBypassDnd() != update.canBypassDnd()) {
@@ -994,8 +1092,7 @@
                 pw.print("  AppSettings: ");
                 pw.print(r.pkg);
                 pw.print(" (");
-                pw.print(r.uid == PackagePreferences.UNKNOWN_UID ? "UNKNOWN_UID"
-                        : Integer.toString(r.uid));
+                pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
                 pw.print(')');
                 if (r.importance != DEFAULT_IMPORTANCE) {
                     pw.print(" importance=");
@@ -1356,8 +1453,6 @@
     }
 
     private static class PackagePreferences {
-        static int UNKNOWN_UID = UserHandle.USER_NULL;
-
         String pkg;
         int uid = UNKNOWN_UID;
         int importance = DEFAULT_IMPORTANCE;
@@ -1366,7 +1461,37 @@
         boolean showBadge = DEFAULT_SHOW_BADGE;
         int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
 
+        Delegate delegate = null;
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
         Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
+
+        public boolean isValidDelegate(String pkg, int uid) {
+            return delegate != null && delegate.isAllowed(pkg, uid);
+        }
+    }
+
+    private static class Delegate {
+        static final boolean DEFAULT_ENABLED = true;
+        static final boolean DEFAULT_USER_ALLOWED = true;
+        String mPkg;
+        int mUid = UNKNOWN_UID;
+        boolean mEnabled = DEFAULT_ENABLED;
+        boolean mUserAllowed = DEFAULT_USER_ALLOWED;
+
+        Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
+            mPkg = pkg;
+            mUid = uid;
+            mEnabled = enabled;
+            mUserAllowed = userAllowed;
+        }
+
+        public boolean isAllowed(String pkg, int uid) {
+            if (pkg == null || uid == UNKNOWN_UID) {
+                return false;
+            }
+            return pkg.equals(mPkg)
+                    && uid == mUid
+                    && (mUserAllowed && mEnabled);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index cf47d4e..bca3ca7 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -40,6 +40,7 @@
 import java.io.FileDescriptor;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -124,7 +125,8 @@
         synchronized (mPackageManagerService.mPackages) {
             // Important: the packages we need to run with ab-ota compiler-reason.
             important = PackageManagerServiceUtils.getPackagesForDexopt(
-                    mPackageManagerService.mPackages.values(), mPackageManagerService);
+                    mPackageManagerService.mPackages.values(), mPackageManagerService,
+                    DEBUG_DEXOPT);
             // Others: we should optimize this with the (first-)boot compiler-reason.
             others = new ArrayList<>(mPackageManagerService.mPackages.values());
             others.removeAll(important);
@@ -157,6 +159,24 @@
         long spaceAvailableNow = getAvailableSpace();
 
         prepareMetricsLogging(important.size(), others.size(), spaceAvailable, spaceAvailableNow);
+
+        if (DEBUG_DEXOPT) {
+            try {
+                // Output some data about the packages.
+                PackageParser.Package lastUsed = Collections.max(important,
+                        (pkg1, pkg2) -> Long.compare(
+                                pkg1.getLatestForegroundPackageUseTimeInMills(),
+                                pkg2.getLatestForegroundPackageUseTimeInMills()));
+                Log.d(TAG, "A/B OTA: lastUsed time = "
+                        + lastUsed.getLatestForegroundPackageUseTimeInMills());
+                Log.d(TAG, "A/B OTA: deprioritized packages:");
+                for (PackageParser.Package pkg : others) {
+                    Log.d(TAG, "  " + pkg.packageName + " - "
+                            + pkg.getLatestForegroundPackageUseTimeInMills());
+                }
+            } catch (Exception ignored) {
+            }
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 1aea8f0..390c0cc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -164,6 +164,13 @@
     public static List<PackageParser.Package> getPackagesForDexopt(
             Collection<PackageParser.Package> packages,
             PackageManagerService packageManagerService) {
+        return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
+    }
+
+    public static List<PackageParser.Package> getPackagesForDexopt(
+            Collection<PackageParser.Package> packages,
+            PackageManagerService packageManagerService,
+            boolean debug) {
         ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
         LinkedList<PackageParser.Package> result = new LinkedList<>();
         ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
@@ -189,14 +196,14 @@
         // TODO: add a property to control this?
         Predicate<PackageParser.Package> remainingPredicate;
         if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
-            if (DEBUG_DEXOPT) {
+            if (debug) {
                 Log.i(TAG, "Looking at historical package use");
             }
             // Get the package that was used last.
             PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
                     Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
                             pkg2.getLatestForegroundPackageUseTimeInMills()));
-            if (DEBUG_DEXOPT) {
+            if (debug) {
                 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
             }
             long estimatedPreviousSystemUseTime =
@@ -218,7 +225,7 @@
         applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
                 packageManagerService);
 
-        if (DEBUG_DEXOPT) {
+        if (debug) {
             Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
             Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs));
         }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c8bd211..479f427 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -55,7 +55,6 @@
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
 import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
@@ -99,7 +98,6 @@
 import com.android.server.FgThread;
 import com.android.server.SystemService;
 
-import java.lang.reflect.InvocationTargetException;
 import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -120,7 +118,6 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
-import com.android.internal.R;
 
 public class WallpaperManagerService extends IWallpaperManager.Stub
         implements IWallpaperManagerService {
@@ -1544,14 +1541,6 @@
         return false;
     }
 
-    private Point getDefaultDisplaySize() {
-        Point p = new Point();
-        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-        Display d = wm.getDefaultDisplay();
-        d.getRealSize(p);
-        return p;
-    }
-
     public void setDimensionHints(int width, int height, String callingPackage)
             throws RemoteException {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
@@ -1564,10 +1553,6 @@
             if (width <= 0 || height <= 0) {
                 throw new IllegalArgumentException("width and height must be > 0");
             }
-            // Make sure it is at least as large as the display.
-            Point displaySize = getDefaultDisplaySize();
-            width = Math.max(width, displaySize.x);
-            height = Math.max(height, displaySize.y);
 
             if (width != wallpaper.width || height != wallpaper.height) {
                 wallpaper.width = width;
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 8fbc01e..9d686ef 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -119,13 +119,23 @@
     private volatile UnicastResponder mUnicastResponder;
 
     public static class RaParams {
+        // Tethered traffic will have the hop limit properly decremented.
+        // Consequently, set the hoplimit greater by one than the upstream
+        // unicast hop limit.
+        //
+        // TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the
+        // upstream interface for more correct behaviour.
+        static final byte DEFAULT_HOPLIMIT = 65;
+
         public boolean hasDefaultRoute;
+        public byte hopLimit;
         public int mtu;
         public HashSet<IpPrefix> prefixes;
         public HashSet<Inet6Address> dnses;
 
         public RaParams() {
             hasDefaultRoute = false;
+            hopLimit = DEFAULT_HOPLIMIT;
             mtu = IPV6_MIN_MTU;
             prefixes = new HashSet<IpPrefix>();
             dnses = new HashSet<Inet6Address>();
@@ -133,6 +143,7 @@
 
         public RaParams(RaParams other) {
             hasDefaultRoute = other.hasDefaultRoute;
+            hopLimit = other.hopLimit;
             mtu = other.mtu;
             prefixes = (HashSet) other.prefixes.clone();
             dnses = (HashSet) other.dnses.clone();
@@ -273,10 +284,12 @@
         final ByteBuffer ra = ByteBuffer.wrap(mRA);
         ra.order(ByteOrder.BIG_ENDIAN);
 
+        final boolean haveRaParams = (mRaParams != null);
         boolean shouldSendRA = false;
 
         try {
-            putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
+            putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute,
+                    haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT);
             putSlla(ra, mInterface.macAddr.toByteArray());
             mRaLength = ra.position();
 
@@ -287,7 +300,7 @@
             //
             // putExpandedFlagsOption(ra);
 
-            if (mRaParams != null) {
+            if (haveRaParams) {
                 putMtu(ra, mRaParams.mtu);
                 mRaLength = ra.position();
 
@@ -348,7 +361,7 @@
     private static byte asByte(int value) { return (byte) value; }
     private static short asShort(int value) { return (short) value; }
 
-    private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute) {
+    private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
         /**
             Router Advertisement Message Format
 
@@ -366,11 +379,10 @@
             |   Options ...
             +-+-+-+-+-+-+-+-+-+-+-+-
         */
-        final byte DEFAULT_HOPLIMIT = 64;
         ra.put(ICMPV6_ND_ROUTER_ADVERT)
           .put(asByte(0))
           .putShort(asShort(0))
-          .put(DEFAULT_HOPLIMIT)
+          .put(hopLimit)
           // RFC 4191 "high" preference, iff. advertising a default route.
           .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
           .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0ff124e..a1b3b98 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -65,6 +65,8 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.Application;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -195,6 +197,8 @@
     IUriGrantsManager mUgm;
     @Mock
     UriGrantsManagerInternal mUgmInternal;
+    @Mock
+    AppOpsManager mAppOpsManager;
 
     // Use a Testable subclass so we can simulate calls from the system without failing.
     private static class TestableNotificationManagerService extends NotificationManagerService {
@@ -295,7 +299,8 @@
                     mListeners, mAssistants, mConditionProviders,
                     mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
                     mGroupHelper, mAm, mAppUsageStats,
-                    mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal);
+                    mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
+                    mAppOpsManager);
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
@@ -531,7 +536,7 @@
         mBinderService.createNotificationChannels(
                 PKG, new ParceledListSlice(Arrays.asList(channel)));
         final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -549,7 +554,7 @@
 
         final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -578,7 +583,7 @@
 
         StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         // The first time a foreground service notification is shown, we allow the channel
@@ -600,7 +605,7 @@
 
         sbn = generateNotificationRecord(channel).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         // The second time it is shown, we keep the user's preference.
@@ -631,7 +636,7 @@
         mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
 
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -645,7 +650,7 @@
 
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -667,7 +672,7 @@
             final StatusBarNotification sbn =
                     generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
             sbn.getNotification().category = category;
-            mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                     sbn.getId(), sbn.getNotification(), sbn.getUserId());
         }
         waitForIdle();
@@ -691,7 +696,7 @@
             final StatusBarNotification sbn =
                     generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
             sbn.getNotification().category = category;
-            mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                     sbn.getId(), sbn.getNotification(), sbn.getUserId());
         }
         waitForIdle();
@@ -714,7 +719,7 @@
             final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
             sbn.getNotification().category = category;
             try {
-                mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+                mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                         sbn.getId(), sbn.getNotification(), sbn.getUserId());
                 fail("Calls from non system apps should not allow use of restricted categories");
             } catch (SecurityException e) {
@@ -746,7 +751,7 @@
 
     @Test
     public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
         StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
@@ -756,7 +761,7 @@
 
     @Test
     public void testCancelNotificationImmediatelyAfterEnqueue() throws Exception {
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
                 generateNotificationRecord(null).getNotification(), 0);
         mBinderService.cancelNotificationWithTag(PKG, "tag", 0, 0);
         waitForIdle();
@@ -768,10 +773,10 @@
 
     @Test
     public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
                 generateNotificationRecord(null).getNotification(), 0);
         mBinderService.cancelNotificationWithTag(PKG, "tag", 0, 0);
         waitForIdle();
@@ -788,7 +793,7 @@
     public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
         NotificationRecord r = generateNotificationRecord(null);
         final StatusBarNotification sbn = r.sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelNotificationsFromListener(null, null);
         waitForIdle();
@@ -801,7 +806,7 @@
     @Test
     public void testCancelAllNotificationsImmediatelyAfterEnqueue() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
@@ -816,7 +821,7 @@
         final NotificationRecord n = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
 
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId());
         waitForIdle();
 
@@ -839,9 +844,9 @@
         final NotificationRecord child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group1", false);
 
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
         waitForIdle();
 
@@ -854,7 +859,7 @@
     public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         for (int i = 0; i < 10; i++) {
-            mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                     sbn.getId(), sbn.getNotification(), sbn.getUserId());
         }
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
@@ -873,17 +878,17 @@
                 mTestNotificationChannel, 2, "group1", false);
 
         // fully post parent notification
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
         waitForIdle();
 
         // enqueue the child several times
         for (int i = 0; i < 10; i++) {
-            mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                     child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
         }
         // make the parent a child, which will cancel the child notification
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
                 parentAsChild.sbn.getUserId());
         waitForIdle();
@@ -895,7 +900,7 @@
     public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
@@ -909,7 +914,7 @@
     public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications("other_pkg_name", sbn.getUserId());
         waitForIdle();
@@ -922,7 +927,7 @@
     @Test
     public void testCancelAllNotifications_NullPkgRemovesAll() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(null, sbn.getUserId());
         waitForIdle();
@@ -935,7 +940,7 @@
     @Test
     public void testCancelAllNotifications_NullPkgIgnoresUserAllNotifications() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), UserHandle.USER_ALL);
         // Null pkg is how we signal a user switch.
         mBinderService.cancelAllNotifications(null, sbn.getUserId());
@@ -950,7 +955,7 @@
     public void testAppInitiatedCancelAllNotifications_CancelsNoClearFlag() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= Notification.FLAG_NO_CLEAR;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
@@ -1037,7 +1042,7 @@
     public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
                 sbn.getUserId());
@@ -1052,10 +1057,10 @@
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags =
                 Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
         waitForIdle();
@@ -1145,21 +1150,21 @@
         // should not be returned
         final NotificationRecord group2 = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group2", true);
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
                 group2.sbn.getId(), group2.sbn.getNotification(), group2.sbn.getUserId());
         waitForIdle();
 
         // should not be returned
         final NotificationRecord nonGroup = generateNotificationRecord(
                 mTestNotificationChannel, 3, null, false);
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
                 nonGroup.sbn.getId(), nonGroup.sbn.getNotification(), nonGroup.sbn.getUserId());
         waitForIdle();
 
         // same group, child, should be returned
         final NotificationRecord group1Child = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group1", false);
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, group1Child.sbn.getId(),
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, null, group1Child.sbn.getId(),
                 group1Child.sbn.getNotification(), group1Child.sbn.getUserId());
         waitForIdle();
 
@@ -1216,7 +1221,7 @@
     public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
@@ -1333,7 +1338,7 @@
                         new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
 
         Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
                 generateNotificationRecord(null, tv).getNotification(), 0);
         verify(mPreferencesHelper, times(1)).getNotificationChannel(
                 anyString(), anyInt(), eq("foo"), anyBoolean());
@@ -1348,7 +1353,7 @@
                 mTestNotificationChannel);
 
         Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
                 generateNotificationRecord(null, tv).getNotification(), 0);
         verify(mPreferencesHelper, times(1)).getNotificationChannel(
                 anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
@@ -1879,7 +1884,7 @@
         final NotificationRecord child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", false);
 
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
                 child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
         waitForIdle();
 
@@ -1892,7 +1897,7 @@
         final NotificationRecord record = generateNotificationRecord(
                 mTestNotificationChannel, 2, null, false);
 
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
                 record.sbn.getId(), record.sbn.getNotification(), record.sbn.getUserId());
         waitForIdle();
 
@@ -1904,7 +1909,7 @@
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", true);
 
-        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
                 parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
         waitForIdle();
 
@@ -2378,12 +2383,12 @@
     @Test
     public void testBumpFGImportance_noChannelChangePreOApp() throws Exception {
         String preOPkg = PKG_N_MR1;
-        int preOUid = 145;
         final ApplicationInfo legacy = new ApplicationInfo();
         legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
         when(mPackageManagerClient.getApplicationInfoAsUser(eq(preOPkg), anyInt(), anyInt()))
                 .thenReturn(legacy);
-        when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt())).thenReturn(preOUid);
+        when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt()))
+                .thenReturn(Binder.getCallingUid());
         getContext().setMockPackageManager(mPackageManagerClient);
 
         Notification.Builder nb = new Notification.Builder(mContext,
@@ -2393,12 +2398,13 @@
                 .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .setPriority(Notification.PRIORITY_MIN);
 
-        StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
-                0, nb.build(), new UserHandle(preOUid), null, 0);
+        StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag",
+                Binder.getCallingUid(), 0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
 
-        mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag",
-                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), sbn.getOpPkg(),
+                sbn.getTag(), sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
+
         assertEquals(IMPORTANCE_LOW,
                 mService.getNotificationRecord(sbn.getKey()).getImportance());
 
@@ -2408,8 +2414,8 @@
                 .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .setPriority(Notification.PRIORITY_MIN);
 
-        sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
-                0, nb.build(), new UserHandle(preOUid), null, 0);
+        sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", Binder.getCallingUid(),
+                0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
 
         mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -3360,7 +3366,7 @@
     }
 
     @Test
-    public void testMybeRecordInterruptionLocked_doesNotRecordTwice()
+    public void testMaybeRecordInterruptionLocked_doesNotRecordTwice()
             throws RemoteException {
         final NotificationRecord r = generateNotificationRecord(
                 mTestNotificationChannel, 1, null, true);
@@ -3373,4 +3379,78 @@
         verify(mAppUsageStats, times(1)).reportInterruptiveNotification(
                 anyString(), anyString(), anyInt());
     }
+
+    @Test
+    public void testResolveNotificationUid_sameApp() throws Exception {
+        ApplicationInfo info = new ApplicationInfo();
+        info.uid = Binder.getCallingUid();
+        when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+
+        int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 0);
+
+        assertEquals(info.uid, actualUid);
+    }
+
+    @Test
+    public void testResolveNotificationUid_sameAppWrongPkg() throws Exception {
+        ApplicationInfo info = new ApplicationInfo();
+        info.uid = Binder.getCallingUid();
+        when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+
+        try {
+            mService.resolveNotificationUid("caller", "other", info.uid, 0);
+            fail("Incorrect pkg didn't throw security exception");
+        } catch (SecurityException e) {
+            // yay
+        }
+    }
+
+    @Test
+    public void testResolveNotificationUid_sameAppWrongUid() throws Exception {
+        ApplicationInfo info = new ApplicationInfo();
+        info.uid = 1356347;
+        when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+
+        try {
+            mService.resolveNotificationUid("caller", "caller", 9, 0);
+            fail("Incorrect uid didn't throw security exception");
+        } catch (SecurityException e) {
+            // yay
+        }
+    }
+
+    @Test
+    public void testResolveNotificationUid_delegateAllowed() throws Exception {
+        int expectedUid = 123;
+
+        when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(expectedUid);
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.isDelegateAllowed(anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(true);
+
+        assertEquals(expectedUid, mService.resolveNotificationUid("caller", "target", 9, 0));
+    }
+
+    @Test
+    public void testResolveNotificationUid_androidAllowed() throws Exception {
+        int expectedUid = 123;
+
+        when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(expectedUid);
+        // no delegate
+
+        assertEquals(expectedUid, mService.resolveNotificationUid("android", "target", 0, 0));
+    }
+
+    @Test
+    public void testResolveNotificationUid_delegateNotAllowed() throws Exception {
+        when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(123);
+        // no delegate
+
+        try {
+            mService.resolveNotificationUid("caller", "target", 9, 0);
+            fail("Incorrect uid didn't throw security exception");
+        } catch (SecurityException e) {
+            // yay
+        }
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 73adf25..750345b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -123,7 +123,6 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        UserHandle user = UserHandle.ALL;
 
         final ApplicationInfo legacy = new ApplicationInfo();
         legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -176,11 +175,6 @@
                 .build();
     }
 
-    private NotificationChannel getDefaultChannel() {
-        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
-                IMPORTANCE_LOW);
-    }
-
     private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, boolean forBackup,
             String... channelIds)
             throws Exception {
@@ -1787,4 +1781,159 @@
         mHelper.setEnabled(PKG_N_MR1, 1000, true);
         assertEquals(3, mHelper.getBlockedAppCount(0));
     }
+
+    @Test
+    public void testSetNotificationDelegate() {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testRevokeNotificationDelegate() {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testRevokeNotificationDelegate_noDelegateExistsNoCrash() {
+        mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testToggleNotificationDelegate() {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
+        assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testToggleNotificationDelegate_noDelegateExistsNoCrash() {
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testIsDelegateAllowed_noSource() {
+        assertFalse(mHelper.isDelegateAllowed("does not exist", -1, "whatever", 0));
+    }
+
+    @Test
+    public void testIsDelegateAllowed_noDelegate() {
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
+
+        assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "whatever", 0));
+    }
+
+    @Test
+    public void testIsDelegateAllowed_delegateDisabledByApp() {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+        assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "other", 53));
+    }
+
+    @Test
+    public void testIsDelegateAllowed_wrongDelegate() {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+        assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "banana", 27));
+    }
+
+    @Test
+    public void testIsDelegateAllowed_delegateDisabledByUser() {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+
+        assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "other", 53));
+    }
+
+    @Test
+    public void testIsDelegateAllowed() {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+
+        assertTrue(mHelper.isDelegateAllowed(PKG_O, UID_O, "other", 53));
+    }
+
+    @Test
+    public void testDelegateXml_noDelegate() throws Exception {
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        loadStreamXml(baos, false);
+
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testDelegateXml_delegate() throws Exception {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        loadStreamXml(baos, false);
+
+        assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testDelegateXml_disabledDelegate() throws Exception {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        loadStreamXml(baos, false);
+
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testDelegateXml_userDisabledDelegate() throws Exception {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        loadStreamXml(baos, false);
+
+        // appears disabled
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+        // but was loaded and can be toggled back on
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
+        assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testDelegateXml_entirelyDisabledDelegate() throws Exception {
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+        mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        loadStreamXml(baos, false);
+
+        // appears disabled
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+        mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+        assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+        mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
+        assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+    }
 }