Merge "DO NOT MERGE Enable hiding system bars by side when IME appears." into rvc-qpr-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index af36260..c273cf08 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5151,10 +5151,18 @@
             bindHeaderChronometerAndTime(contentView, p);
             bindProfileBadge(contentView, p);
             bindAlertedIcon(contentView, p);
+            bindActivePermissions(contentView, p);
             bindExpandButton(contentView, p);
             mN.mUsesStandardHeader = true;
         }
 
+        private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
+            int color = getNeutralColor(p);
+            contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
+            contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
+            contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
+        }
+
         private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
             int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
             contentView.setDrawableTint(R.id.expand_button, false, color,
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 7a467d6..0c50cb7 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -52,11 +52,13 @@
     private View mHeaderText;
     private View mSecondaryHeaderText;
     private OnClickListener mExpandClickListener;
+    private OnClickListener mAppOpsListener;
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
     private LinearLayout mTransferChip;
     private NotificationExpandButton mExpandButton;
     private CachingIconView mIcon;
     private View mProfileBadge;
+    private View mAppOps;
     private boolean mExpanded;
     private boolean mShowExpandButtonAtEnd;
     private boolean mShowWorkBadgeAtEnd;
@@ -113,6 +115,7 @@
         mExpandButton = findViewById(com.android.internal.R.id.expand_button);
         mIcon = findViewById(com.android.internal.R.id.icon);
         mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
+        mAppOps = findViewById(com.android.internal.R.id.app_ops);
     }
 
     @Override
@@ -140,6 +143,7 @@
             // Icons that should go at the end
             if ((child == mExpandButton && mShowExpandButtonAtEnd)
                     || child == mProfileBadge
+                    || child == mAppOps
                     || child == mTransferChip) {
                 iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
             } else {
@@ -204,6 +208,7 @@
             // Icons that should go at the end
             if ((child == mExpandButton && mShowExpandButtonAtEnd)
                     || child == mProfileBadge
+                    || child == mAppOps
                     || child == mTransferChip) {
                 if (end == getMeasuredWidth()) {
                     layoutRight = end - mContentEndMargin;
@@ -272,10 +277,22 @@
     }
 
     private void updateTouchListener() {
+        if (mExpandClickListener == null && mAppOpsListener == null) {
+            setOnTouchListener(null);
+            return;
+        }
         setOnTouchListener(mTouchListener);
         mTouchListener.bindTouchRects();
     }
 
+    /**
+     * Sets onclick listener for app ops icons.
+     */
+    public void setAppOpsOnClickListener(OnClickListener l) {
+        mAppOpsListener = l;
+        updateTouchListener();
+    }
+
     @Override
     public void setOnClickListener(@Nullable OnClickListener l) {
         mExpandClickListener = l;
@@ -363,6 +380,7 @@
 
         private final ArrayList<Rect> mTouchRects = new ArrayList<>();
         private Rect mExpandButtonRect;
+        private Rect mAppOpsRect;
         private int mTouchSlop;
         private boolean mTrackGesture;
         private float mDownX;
@@ -375,6 +393,8 @@
             mTouchRects.clear();
             addRectAroundView(mIcon);
             mExpandButtonRect = addRectAroundView(mExpandButton);
+            mAppOpsRect = addRectAroundView(mAppOps);
+            setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps));
             addWidthRect();
             mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
         }
@@ -435,6 +455,11 @@
                     break;
                 case MotionEvent.ACTION_UP:
                     if (mTrackGesture) {
+                        if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y)
+                                || mAppOpsRect.contains((int) mDownX, (int) mDownY))) {
+                            mAppOps.performClick();
+                            return true;
+                        }
                         mExpandButton.performClick();
                     }
                     break;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 970d70c..f56c357 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -52,6 +52,7 @@
 import android.view.ViewDebug;
 import android.view.ViewHierarchyEncoder;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
@@ -306,9 +307,6 @@
         setMax(a.getInt(R.styleable.ProgressBar_max, mMax));
 
         setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));
-        // onProgressRefresh() is only called when the progress changes. So we should set
-        // stateDescription during initialization here.
-        super.setStateDescription(formatStateDescription(mProgress));
 
         setSecondaryProgress(a.getInt(
                 R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
@@ -1601,7 +1599,8 @@
     }
 
     void onProgressRefresh(float scale, boolean fromUser, int progress) {
-        if (mCustomStateDescription == null) {
+        if (AccessibilityManager.getInstance(mContext).isEnabled()
+                && mCustomStateDescription == null) {
             super.setStateDescription(formatStateDescription(mProgress));
         }
     }
@@ -2325,6 +2324,7 @@
                     AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(),
                     getProgress());
             info.setRangeInfo(rangeInfo);
+            info.setStateDescription(formatStateDescription(mProgress));
         }
     }
 
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index e3a456c..0791ed3 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -167,6 +167,8 @@
     private int mFacePileProtectionWidthExpanded;
     private boolean mImportantConversation;
     private TextView mUnreadBadge;
+    private ViewGroup mAppOps;
+    private Rect mAppOpsTouchRect = new Rect();
     private float mMinTouchSize;
     private Icon mConversationIcon;
     private Icon mShortcutIcon;
@@ -208,6 +210,7 @@
         mConversationIconView = findViewById(R.id.conversation_icon);
         mConversationIconContainer = findViewById(R.id.conversation_icon_container);
         mIcon = findViewById(R.id.icon);
+        mAppOps = findViewById(com.android.internal.R.id.app_ops);
         mMinTouchSize = 48 * getResources().getDisplayMetrics().density;
         mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring);
         mConversationIconBadge = findViewById(R.id.conversation_icon_badge);
@@ -1163,6 +1166,47 @@
                 }
             });
         }
+        if (mAppOps.getWidth() > 0) {
+
+            // Let's increase the touch size of the app ops view if it's here
+            mAppOpsTouchRect.set(
+                    mAppOps.getLeft(),
+                    mAppOps.getTop(),
+                    mAppOps.getRight(),
+                    mAppOps.getBottom());
+            for (int i = 0; i < mAppOps.getChildCount(); i++) {
+                View child = mAppOps.getChildAt(i);
+                if (child.getVisibility() == GONE) {
+                    continue;
+                }
+                // Make sure each child has at least a minTouchSize touch target around it
+                float childTouchLeft = child.getLeft() + child.getWidth() / 2.0f
+                        - mMinTouchSize / 2.0f;
+                float childTouchRight = childTouchLeft + mMinTouchSize;
+                mAppOpsTouchRect.left = (int) Math.min(mAppOpsTouchRect.left,
+                        mAppOps.getLeft() + childTouchLeft);
+                mAppOpsTouchRect.right = (int) Math.max(mAppOpsTouchRect.right,
+                        mAppOps.getLeft() + childTouchRight);
+            }
+
+            // Increase the height
+            int heightIncrease = 0;
+            if (mAppOpsTouchRect.height() < mMinTouchSize) {
+                heightIncrease = (int) Math.ceil((mMinTouchSize - mAppOpsTouchRect.height())
+                        / 2.0f);
+            }
+            mAppOpsTouchRect.inset(0, -heightIncrease);
+
+            // Let's adjust the hitrect since app ops isn't a direct child
+            ViewGroup viewGroup = (ViewGroup) mAppOps.getParent();
+            while (viewGroup != this) {
+                mAppOpsTouchRect.offset(viewGroup.getLeft(), viewGroup.getTop());
+                viewGroup = (ViewGroup) viewGroup.getParent();
+            }
+            //
+            // Extend the size of the app opps to be at least 48dp
+            setTouchDelegate(new TouchDelegate(mAppOpsTouchRect, mAppOps));
+        }
     }
 
     public MessagingLinearLayout getMessagingLinearLayout() {
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 03e130e..23b8bd3 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -146,6 +146,43 @@
         android:visibility="gone"
         android:contentDescription="@string/notification_work_profile_content_description"
         />
+    <LinearLayout
+        android:id="@+id/app_ops"
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:layout_marginStart="6dp"
+        android:background="?android:selectableItemBackgroundBorderless"
+        android:orientation="horizontal">
+        <ImageView
+            android:id="@+id/camera"
+            android:layout_width="?attr/notificationHeaderIconSize"
+            android:layout_height="?attr/notificationHeaderIconSize"
+            android:src="@drawable/ic_camera"
+            android:visibility="gone"
+            android:focusable="false"
+            android:contentDescription="@string/notification_appops_camera_active"
+            />
+        <ImageView
+            android:id="@+id/mic"
+            android:layout_width="?attr/notificationHeaderIconSize"
+            android:layout_height="?attr/notificationHeaderIconSize"
+            android:src="@drawable/ic_mic"
+            android:layout_marginStart="4dp"
+            android:visibility="gone"
+            android:focusable="false"
+            android:contentDescription="@string/notification_appops_microphone_active"
+            />
+        <ImageView
+            android:id="@+id/overlay"
+            android:layout_width="?attr/notificationHeaderIconSize"
+            android:layout_height="?attr/notificationHeaderIconSize"
+            android:src="@drawable/ic_alert_window_layer"
+            android:layout_marginStart="4dp"
+            android:visibility="gone"
+            android:focusable="false"
+            android:contentDescription="@string/notification_appops_overlay_active"
+            />
+    </LinearLayout>
     <include
         layout="@layout/notification_material_media_transfer_action"
         android:id="@+id/media_seamless"
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 6978a9a..f500147 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -270,9 +270,9 @@
     <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"وضع صامت"</string>
     <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"الصوت متوقف"</string>
     <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"الصوت قيد التفعيل"</string>
-    <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"وضع الطائرة"</string>
-    <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"وضع الطائرة قيد التفعيل"</string>
-    <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"وضع الطائرة متوقف"</string>
+    <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"وضع الطيران"</string>
+    <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"وضع الطيران قيد التفعيل"</string>
+    <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"وضع الطيران متوقف"</string>
     <string name="global_action_settings" msgid="4671878836947494217">"الإعدادات"</string>
     <string name="global_action_assist" msgid="2517047220311505805">"مساعدة"</string>
     <string name="global_action_voice_assist" msgid="6655788068555086695">"المساعد الصوتي"</string>
@@ -398,7 +398,7 @@
     <string name="permlab_getPackageSize" msgid="375391550792886641">"قياس مساحة تخزين التطبيق"</string>
     <string name="permdesc_getPackageSize" msgid="742743530909966782">"للسماح للتطبيق باسترداد شفرته وبياناته وأحجام ذاكرات التخزين المؤقت"</string>
     <string name="permlab_writeSettings" msgid="8057285063719277394">"تعديل إعدادات النظام"</string>
-    <string name="permdesc_writeSettings" msgid="8293047411196067188">"للسماح للتطبيق بتعديل بيانات إعدادات النظام. يمكن أن تتلف التطبيقات الضارة تهيئة نظامك."</string>
+    <string name="permdesc_writeSettings" msgid="8293047411196067188">"للسماح للتطبيق بتعديل بيانات إعدادات النظام. يمكن أن تتلف التطبيقات الضارة إعداد نظامك."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"العمل عند بدء التشغيل"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"للسماح للتطبيق ببدء تشغيل نفسه عقب انتهاء النظام من التشغيل. قد يؤدي ذلك إلى استغراق المزيد من الوقت عند بدء الجهاز اللوحي والسماح للتطبيق بإبطاء الأداء الإجمالي للجهاز اللوحي من خلال تشغيله دائمًا."</string>
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"‏للسماح بتشغيل التطبيق تلقائيًا بعد الانتهاء من بدء تشغيل النظام. وقد يؤدي ذلك إلى إطالة فترة بدء تشغيل جهاز Android TV، بالإضافة إلى أنه يسمح للتطبيق بإبطاء أداء الجهاز بشكل عام لأنه يتم تشغيله بشكل دائم."</string>
@@ -507,7 +507,7 @@
     <string name="permlab_accessWifiState" msgid="5552488500317911052">"‏عرض اتصالات Wi-Fi"</string>
     <string name="permdesc_accessWifiState" msgid="6913641669259483363">"‏للسماح للتطبيق بعرض معلومات حول شبكات Wi-Fi، كعرض معلومات حول ما إذا تم تفعيل Wi-Fi واسم أجهزة Wi-Fi المتصلة."</string>
     <string name="permlab_changeWifiState" msgid="7947824109713181554">"‏التوصيل والفصل من Wi-Fi"</string>
-    <string name="permdesc_changeWifiState" msgid="7170350070554505384">"‏للسماح للتطبيق بالاتصال بنقاط الوصول إلى Wi-Fi وقطع الاتصال بها، وإجراء تغييرات على تهيئة الجهاز لشبكات Wi-Fi."</string>
+    <string name="permdesc_changeWifiState" msgid="7170350070554505384">"‏للسماح للتطبيق بالاتصال بنقاط الوصول إلى Wi-Fi وقطع الاتصال بها، وإجراء تغييرات على إعداد الجهاز لشبكات Wi-Fi."</string>
     <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"‏السماح باستقبال بث Wi-Fi متعدد"</string>
     <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"‏للسماح للتطبيق بتلقي الحزم التي يتم إرسالها إلى جميع الأجهزة على شبكة Wi-Fi باستخدام عناوين بث متعدد، وليس باستخدام جهازك اللوحي فقط. ويؤدي ذلك إلى استخدام قدر أكبر من الطاقة يفوق وضع البث غير المتعدد."</string>
     <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"‏للسماح للتطبيق بتلقّي الحِزم التي يتم إرسالها إلى جميع الأجهزة على شبكة Wi-Fi باستخدام عناوين بث متعدد، وليس باستخدام جهاز Android TV فقط. ويؤدي ذلك إلى استخدام قدر أكبر من الطاقة يفوق ما يتم استهلاكه في وضع البث غير المتعدد."</string>
@@ -661,8 +661,8 @@
     <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"للسماح للمالك بالربط بواجهة المستوى العلوي لخدمة موفر الحالة. لن تكون هناك حاجة إلى هذا الإعداد مطلقًا مع التطبيقات العادية."</string>
     <string name="permlab_bindDreamService" msgid="4776175992848982706">"‏الالتزام بخدمة dream"</string>
     <string name="permdesc_bindDreamService" msgid="9129615743300572973">"‏للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة dream. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
-    <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"استدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال"</string>
-    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"للسماح للمالك باستدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+    <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"استدعاء تطبيق الإعداد الذي يوفره مشغل شبكة الجوال"</string>
+    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"للسماح للمالك باستدعاء تطبيق الإعداد الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
     <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"الاستماع إلى ملاحظات حول أحوال الشبكة"</string>
     <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"للسماح للتطبيق بالاستماع إلى ملاحظات حول أحوال الشبكة. لا حاجة إلى هذا مع التطبيقات العادية."</string>
     <string name="permlab_setInputCalibration" msgid="932069700285223434">"تغيير معايرة أجهزة الإدخال"</string>
@@ -678,7 +678,7 @@
     <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"الالتزام بخدمات مشغل شبكة الجوال"</string>
     <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"للسماح للمالك بالالتزام بخدمات مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
     <string name="permlab_access_notification_policy" msgid="5524112842876975537">"الوصول إلى إعداد \"عدم الإزعاج\""</string>
-    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة تهيئة \"عدم الإزعاج\" وكتابتها."</string>
+    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"تعيين قواعد كلمة المرور"</string>
@@ -1462,7 +1462,7 @@
     <string name="ext_media_status_unmountable" msgid="7043574843541087748">"تالف"</string>
     <string name="ext_media_status_unsupported" msgid="5460509911660539317">"غير متوافق"</string>
     <string name="ext_media_status_ejecting" msgid="7532403368044013797">"جارٍ إنهاء التحميل…"</string>
-    <string name="ext_media_status_formatting" msgid="774148701503179906">"جارٍ التهيئة…"</string>
+    <string name="ext_media_status_formatting" msgid="774148701503179906">"تجري التهيئة..."</string>
     <string name="ext_media_status_missing" msgid="6520746443048867314">"لم يتم الإدخال"</string>
     <string name="activity_list_empty" msgid="4219430010716034252">"لم يتم العثور على أي أنشطة متطابقة."</string>
     <string name="permlab_route_media_output" msgid="8048124531439513118">"توجيه إخراج الوسائط"</string>
@@ -2155,7 +2155,7 @@
     <string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"جدول بيانات: <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
     <string name="mime_type_presentation" msgid="1145384236788242075">"عرض تقديمي"</string>
     <string name="mime_type_presentation_ext" msgid="8761049335564371468">"عرض تقديمي: <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
-    <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"سيظل البلوتوث مفعَّلاً أثناء استخدام \"وضع الطائرة\"."</string>
+    <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"سيظل البلوتوث مفعَّلاً أثناء استخدام \"وضع الطيران\"."</string>
     <string name="car_loading_profile" msgid="8219978381196748070">"جارٍ التحميل"</string>
     <plurals name="file_count" formatted="false" msgid="7063513834724389247">
       <item quantity="zero"><xliff:g id="FILE_NAME_2">%s</xliff:g> و<xliff:g id="COUNT_3">%d</xliff:g> ملف</item>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 02dd4f2..32495371 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -970,7 +970,7 @@
     <string name="save_password_remember" msgid="6490888932657708341">"Zapamti"</string>
     <string name="save_password_never" msgid="6776808375903410659">"Nikad"</string>
     <string name="open_permission_deny" msgid="5136793905306987251">"Nemate odobrenje za otvaranje ove stranice."</string>
-    <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međuspremnik."</string>
+    <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međumemoriju."</string>
     <string name="copied" msgid="4675902854553014676">"Kopirano"</string>
     <string name="more_item_label" msgid="7419249600215749115">"Više"</string>
     <string name="prepend_shortcut_label" msgid="1743716737502867951">"Meni+"</string>
@@ -1280,7 +1280,7 @@
     <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string>
     <string name="accept" msgid="5447154347815825107">"Prihvati"</string>
     <string name="decline" msgid="6490507610282145874">"Odbijte"</string>
-    <string name="select_character" msgid="3352797107930786979">"Umetni karakter"</string>
+    <string name="select_character" msgid="3352797107930786979">"Umetni znak"</string>
     <string name="sms_control_title" msgid="4748684259903148341">"Slanje SMS poruka"</string>
     <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje veliki broj SMS poruka. Da li želite dozvoliti ovoj aplikaciji da nastavi slanje poruka?"</string>
     <string name="sms_control_yes" msgid="4858845109269524622">"Dozvoli"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b8c00d0..1024729 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -421,7 +421,7 @@
     <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Esta app puede agregar, quitar o cambiar eventos del calendario en tu teléfono. Puede enviar mensajes que parecen proceder de propietarios del calendario o cambiar eventos sin notificarlos."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"acceder a comandos adicionales del proveedor del lugar"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Permite que la aplicación acceda a comandos adicionales del proveedor de ubicación. Esto puede permitirle a la aplicación interferir con el funcionamiento del GPS o de otras fuentes de ubicación."</string>
-    <string name="permlab_accessFineLocation" msgid="6426318438195622966">"acceder a la ubicación exacta solo en primer plano"</string>
+    <string name="permlab_accessFineLocation" msgid="6426318438195622966">"acceder a la ubicación precisa solo en primer plano"</string>
     <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Mientras la usas, esta app puede obtener tu ubicación exacta mediante los Servicios de ubicación, siempre y cuando el dispositivo los tenga activados. Es posible que esto aumente el uso de batería."</string>
     <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"acceder a la ubicación aproximada solo en primer plano"</string>
     <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Mientras la usas, esta app puede obtener tu ubicación aproximada mediante los Servicios de ubicación, siempre y cuando el dispositivo los tenga activados."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index e5600b0..b7a332e 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -849,7 +849,7 @@
     <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausar"</string>
     <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Reproducir"</string>
     <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Deter"</string>
-    <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Rebobinar"</string>
+    <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Retroceder"</string>
     <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Avance rápido"</string>
     <string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emerxencia"</string>
     <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Bloqueada pola rede"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index b66d7cb..34e1db5e 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -307,7 +307,7 @@
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"hanganyag rögzítése"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Testmozgás"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"hozzáférés a testmozgási adatokhoz"</string>
-    <string name="permgrouplab_camera" msgid="9090413408963547706">"Fényképezőgép"</string>
+    <string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"fotók és videók készítése"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"Hívásnaplók"</string>
     <string name="permgroupdesc_calllog" msgid="2026996642917801803">"hívásnapló olvasása és írása"</string>
@@ -1900,8 +1900,8 @@
     <string name="profile_encrypted_message" msgid="1128512616293157802">"A feloldáshoz koppintson rá"</string>
     <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Csatlakoztatva a(z) <xliff:g id="PRODUCT_NAME">%1$s</xliff:g> eszközhöz"</string>
     <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Koppintson ide a fájlok megtekintéséhez"</string>
-    <string name="pin_target" msgid="8036028973110156895">"Rögzítés"</string>
-    <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> rögzítése"</string>
+    <string name="pin_target" msgid="8036028973110156895">"Kitűzés"</string>
+    <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> kitűzése"</string>
     <string name="unpin_target" msgid="3963318576590204447">"Feloldás"</string>
     <string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> rögzítésének feloldása"</string>
     <string name="app_info" msgid="6113278084877079851">"Alkalmazásinformáció"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index b531d02..30b092d 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1979,7 +1979,7 @@
     <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Չհաջողվեց վերականգնել դյուրանցումը, քանի որ հավելվածների ստորագրությունները տարբեր են"</string>
     <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Չհաջողվեց վերականգնել դյուրանցումը"</string>
     <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Դյուրանցումն անջատված է"</string>
-    <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ՀԵՌԱՑՆԵԼ"</string>
+    <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ԱՊԱՏԵՂԱԴՐԵԼ"</string>
     <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"ԲԱՑԵԼ"</string>
     <string name="harmful_app_warning_title" msgid="8794823880881113856">"Հայտնաբերվել է վնասաբեր հավելված"</string>
     <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> հավելվածն ուզում է ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index edc4de6..1517c17 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1317,7 +1317,7 @@
     <string name="console_running_notification_title" msgid="6087888939261635904">"Сериялык консоль иштетилди"</string>
     <string name="console_running_notification_message" msgid="7892751888125174039">"Майнаптуулугуна таасири тиет. Аны өчүрүү үчүн операциялык тутумду жүктөгүчтү текшериңиз."</string>
     <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"USB портунда суюктук же урандылар бар"</string>
-    <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт автоматтык түрдө өчүрүлдү. Кененирээк маалымат алуу үчүн, таптап коюңуз."</string>
+    <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт автоматтык түрдө өчтү. Кененирээк маалымат алуу үчүн, таптап коюңуз."</string>
     <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"USB портун колдонууга болот"</string>
     <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"Телефон суюктук менен урандыларды аныктаган жок."</string>
     <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"Мүчүлүштүк тууралуу кабар алынууда…"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 6af5d5e..3833605f 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1117,7 +1117,7 @@
     <string name="yes" msgid="9069828999585032361">"ОК"</string>
     <string name="no" msgid="5122037903299899715">"Цуцлах"</string>
     <string name="dialog_alert_title" msgid="651856561974090712">"Анхаар"</string>
-    <string name="loading" msgid="3138021523725055037">"Ачааллаж байна..."</string>
+    <string name="loading" msgid="3138021523725055037">"Ачаалж байна..."</string>
     <string name="capital_on" msgid="2770685323900821829">"Идэвхтэй"</string>
     <string name="capital_off" msgid="7443704171014626777">"Идэвхгүй"</string>
     <string name="checked" msgid="9179896827054513119">"тэмдэглэсэн"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 95d5f8f..439a1a3 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1276,7 +1276,7 @@
     <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nooit toestaan"</string>
     <string name="sim_removed_title" msgid="5387212933992546283">"Simkaart verwijderd"</string>
     <string name="sim_removed_message" msgid="9051174064474904617">"Het mobiele netwerk is niet beschikbaar totdat u het apparaat opnieuw start met een geldige simkaart."</string>
-    <string name="sim_done_button" msgid="6464250841528410598">"Gereed"</string>
+    <string name="sim_done_button" msgid="6464250841528410598">"Klaar"</string>
     <string name="sim_added_title" msgid="7930779986759414595">"Simkaart aangesloten"</string>
     <string name="sim_added_message" msgid="6602906609509958680">"Start je apparaat opnieuw voor toegang tot het mobiele netwerk."</string>
     <string name="sim_restart_button" msgid="8481803851341190038">"Opnieuw starten"</string>
@@ -1289,7 +1289,7 @@
     <string name="time_picker_dialog_title" msgid="9053376764985220821">"Tijd instellen"</string>
     <string name="date_picker_dialog_title" msgid="5030520449243071926">"Datum instellen"</string>
     <string name="date_time_set" msgid="4603445265164486816">"Instellen"</string>
-    <string name="date_time_done" msgid="8363155889402873463">"Gereed"</string>
+    <string name="date_time_done" msgid="8363155889402873463">"Klaar"</string>
     <string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"NIEUW: "</font></string>
     <string name="perms_description_app" msgid="2747752389870161996">"Geleverd door <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="no_permissions" msgid="5729199278862516390">"Geen rechten nodig"</string>
@@ -1376,7 +1376,7 @@
     <string name="ext_media_status_removed" msgid="241223931135751691">"Verwijderd"</string>
     <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Uitgeworpen"</string>
     <string name="ext_media_status_checking" msgid="159013362442090347">"Controleren…"</string>
-    <string name="ext_media_status_mounted" msgid="3459448555811203459">"Gereed"</string>
+    <string name="ext_media_status_mounted" msgid="3459448555811203459">"Klaar"</string>
     <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Alleen lezen"</string>
     <string name="ext_media_status_bad_removal" msgid="508448566481406245">"Onveilig verwijderd"</string>
     <string name="ext_media_status_unmountable" msgid="7043574843541087748">"Beschadigd"</string>
@@ -1401,7 +1401,7 @@
     <string name="ime_action_search" msgid="4501435960587287668">"Zoeken"</string>
     <string name="ime_action_send" msgid="8456843745664334138">"Verzenden"</string>
     <string name="ime_action_next" msgid="4169702997635728543">"Volgende"</string>
-    <string name="ime_action_done" msgid="6299921014822891569">"Gereed"</string>
+    <string name="ime_action_done" msgid="6299921014822891569">"Klaar"</string>
     <string name="ime_action_previous" msgid="6548799326860401611">"Vorige"</string>
     <string name="ime_action_default" msgid="8265027027659800121">"Uitvoeren"</string>
     <string name="dial_number_using" msgid="6060769078933953531">"Nummer bellen\nmet <xliff:g id="NUMBER">%s</xliff:g>"</string>
@@ -1448,7 +1448,7 @@
       <item quantity="other"><xliff:g id="INDEX">%d</xliff:g> van <xliff:g id="TOTAL">%d</xliff:g></item>
       <item quantity="one">1 overeenkomst</item>
     </plurals>
-    <string name="action_mode_done" msgid="2536182504764803222">"Gereed"</string>
+    <string name="action_mode_done" msgid="2536182504764803222">"Klaar"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Gedeelde opslag wissen…"</string>
     <string name="share" msgid="4157615043345227321">"Delen"</string>
     <string name="find" msgid="5015737188624767706">"Vinden"</string>
@@ -1492,7 +1492,7 @@
     <string name="keyboardview_keycode_alt" msgid="8997420058584292385">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Annuleren"</string>
     <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Delete"</string>
-    <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Gereed"</string>
+    <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Klaar"</string>
     <string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Modus wijzigen"</string>
     <string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string>
     <string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string>
@@ -1645,7 +1645,7 @@
     <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Functies kiezen voor gebruik met de sneltoets via de volumeknop"</string>
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is uitgeschakeld"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Snelkoppelingen bewerken"</string>
-    <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gereed"</string>
+    <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string>
     <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Sneltoets uitschakelen"</string>
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string>
@@ -1774,7 +1774,7 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Volledig scherm wordt weergegeven"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ik snap het"</string>
-    <string name="done_label" msgid="7283767013231718521">"Gereed"</string>
+    <string name="done_label" msgid="7283767013231718521">"Klaar"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Ronde schuifregelaar voor uren"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Ronde schuifregelaar voor minuten"</string>
     <string name="select_hours" msgid="5982889657313147347">"Uren selecteren"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index fe647cb..279c619 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1829,7 +1829,7 @@
       <item quantity="other">I %d tim</item>
       <item quantity="one">I en 1 tim</item>
     </plurals>
-    <string name="zen_mode_until" msgid="2250286190237669079">"Till kl. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+    <string name="zen_mode_until" msgid="2250286190237669079">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="7046911727540499275">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nästa alarm)"</string>
     <string name="zen_mode_forever" msgid="740585666364912448">"Tills du stänger av"</string>
     <string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Tills du inaktiverar Stör ej"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8276905..e168ae4 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -303,7 +303,7 @@
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"itume na iangalie SMS"</string>
     <string name="permgrouplab_storage" msgid="1938416135375282333">"Faili na maudhui"</string>
     <string name="permgroupdesc_storage" msgid="6351503740613026600">"ifikie picha, maudhui na faili kwenye kifaa chako"</string>
-    <string name="permgrouplab_microphone" msgid="2480597427667420076">"Kipokea sauti"</string>
+    <string name="permgrouplab_microphone" msgid="2480597427667420076">"Maikrofoni"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"irekodi sauti"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Shughuli za kimwili"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"ifikie shughuli zako za kimwili"</string>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
index 3f6a08c..694ae6d9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
@@ -253,14 +253,14 @@
             e.printStackTrace();
         }
 
-        if (!mTopNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom(
-                TopNotificationPanelViewMediator.class)) {
+        if (!mTopNavBarEnabled && TopNotificationPanelViewMediator.class.isAssignableFrom(
+                notificationPanelMediatorUsed)) {
             throw new RuntimeException(
                     "Top System Bar must be enabled to use " + notificationPanelMediatorName);
         }
 
-        if (!mBottomNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom(
-                BottomNotificationPanelViewMediator.class)) {
+        if (!mBottomNavBarEnabled && BottomNotificationPanelViewMediator.class.isAssignableFrom(
+                notificationPanelMediatorUsed)) {
             throw new RuntimeException("Bottom System Bar must be enabled to use "
                     + notificationPanelMediatorName);
         }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
index 41b0d78..96f0504 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
@@ -32,7 +32,14 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarDeviceProvisionedController;
 import com.android.systemui.car.CarSystemUiTest;
+import com.android.systemui.car.notification.NotificationPanelViewController;
+import com.android.systemui.car.notification.NotificationPanelViewMediator;
+import com.android.systemui.car.notification.PowerManagerHelper;
+import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -104,6 +111,33 @@
     }
 
     @Test
+    public void onInit_topNotifPanelViewMediatorUsed_topBarEnabled_doesNotThrowException() {
+        when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(true);
+        when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+                TestTopNotificationPanelViewMediator.class.getName());
+
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void onInit_topNotifPanelViewMediatorUsed_topBarNotEnabled_throwsRuntimeException() {
+        when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false);
+        when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+                TestTopNotificationPanelViewMediator.class.getName());
+
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+    }
+
+    @Test
+    public void onInit_notificationPanelViewMediatorUsed_topBarNotEnabled_doesNotThrowException() {
+        when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false);
+        when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+                NotificationPanelViewMediator.class.getName());
+
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+    }
+
+    @Test
     public void getTopSystemBarLayoutParams_topBarEnabled_returnsTopSystemBarLayoutParams() {
         mSystemBarConfigs = new SystemBarConfigs(mResources);
         WindowManager.LayoutParams lp = mSystemBarConfigs.getLayoutParamsBySide(
@@ -199,4 +233,20 @@
         when(mResources.getBoolean(R.bool.config_hideRightSystemBarForKeyboard)).thenReturn(
                 false);
     }
+
+    // Intentionally using a subclass of TopNotificationPanelViewMediator for testing purposes to
+    // ensure that OEM's will be able to implement and use their own NotificationPanelViewMediator.
+    private class TestTopNotificationPanelViewMediator extends
+            TopNotificationPanelViewMediator {
+        TestTopNotificationPanelViewMediator(
+                CarNavigationBarController carNavigationBarController,
+                NotificationPanelViewController notificationPanelViewController,
+                PowerManagerHelper powerManagerHelper,
+                BroadcastDispatcher broadcastDispatcher,
+                CarDeviceProvisionedController carDeviceProvisionedController,
+                ConfigurationController configurationController) {
+            super(carNavigationBarController, notificationPanelViewController, powerManagerHelper,
+                    broadcastDispatcher, carDeviceProvisionedController, configurationController);
+        }
+    }
 }
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index 87f89ce..c840374 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -57,7 +57,7 @@
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"هل تريد إزالة هذا التطبيق "<b>"لكل"</b>" المستخدمين؟ ستتم إزالة التطبيق وبياناته من "<b>"كل"</b>" المستخدمين على هذا الجهاز."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"هل تريد إزالة هذا التطبيق للمستخدم <xliff:g id="USERNAME">%1$s</xliff:g>؟"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات."</string>
-    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات. وسيؤثر هذا في جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string>
+    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد إعادة ضبط هذا التطبيق على الإعدادات الأصلية؟ سؤدي ذلك إلى إزالة جميع البيانات، كما سيؤثر على جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string>
     <string name="uninstall_keep_data" msgid="7002379587465487550">"الاحتفاظ بالحجم <xliff:g id="SIZE">%1$s</xliff:g> من بيانات التطبيق."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"عمليات إلغاء التثبيت الجارية"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"عمليات إلغاء التثبيت غير الناجحة"</string>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index 108c86f..d3a9589 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="7488448184431507488">"Pakket-installatie"</string>
     <string name="install" msgid="711829760615509273">"Installeren"</string>
-    <string name="done" msgid="6632441120016885253">"Gereed"</string>
+    <string name="done" msgid="6632441120016885253">"Klaar"</string>
     <string name="cancel" msgid="1018267193425558088">"Annuleren"</string>
     <string name="installing" msgid="4921993079741206516">"Installeren…"</string>
     <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> installeren…"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 9d06c84..72a6074 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -465,7 +465,16 @@
             synchronized (mMediaDevicesLock) {
                 mMediaDevices.clear();
                 mMediaDevices.addAll(devices);
-                mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
+                // Add disconnected bluetooth devices only when phone output device is available.
+                for (MediaDevice device : devices) {
+                    final int type = device.getDeviceType();
+                    if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE
+                            || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
+                            || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) {
+                        mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
+                        break;
+                    }
+                }
             }
 
             final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index a654fd4..8e850b2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -585,6 +585,7 @@
         when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
         when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
         when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
+        when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
         when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
@@ -683,6 +684,7 @@
         when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
         when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
         when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
+        when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
         when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index 7463d3f..8f24e79 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -75,6 +75,11 @@
     public MenuItem getLongpressMenuItem(Context context);
 
     /**
+     * @return the {@link MenuItem} to display when app ops icons are pressed.
+     */
+    public MenuItem getAppOpsMenuItem(Context context);
+
+    /**
      * @return the {@link MenuItem} to display when snooze item is pressed.
      */
     public MenuItem getSnoozeMenuItem(Context context);
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 6d86a78..65e3f0d 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -112,7 +112,7 @@
     <string name="kg_pin_accepted" msgid="1625501841604389716">"تم قبول الرمز"</string>
     <string name="keyguard_carrier_default" msgid="6359808469637388586">"لا تتوفر خدمة."</string>
     <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تبديل أسلوب الإدخال"</string>
-    <string name="airplane_mode" msgid="2528005343938497866">"وضع الطائرة"</string>
+    <string name="airplane_mode" msgid="2528005343938497866">"وضع الطيران"</string>
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"يجب رسم النقش بعد إعادة تشغيل الجهاز"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"يجب إدخال رقم التعريف الشخصي بعد إعادة تشغيل الجهاز"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"يجب إدخال كلمة المرور بعد إعادة تشغيل الجهاز"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index 5f88156..2deeb12 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -39,15 +39,21 @@
  */
 @Singleton
 public class ForegroundServiceController {
-    public static final int[] APP_OPS = new int[] {AppOpsManager.OP_SYSTEM_ALERT_WINDOW};
+    public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
+            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+            AppOpsManager.OP_RECORD_AUDIO,
+            AppOpsManager.OP_COARSE_LOCATION,
+            AppOpsManager.OP_FINE_LOCATION};
 
     private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
     private final Object mMutex = new Object();
+    private final NotificationEntryManager mEntryManager;
     private final Handler mMainHandler;
 
     @Inject
-    public ForegroundServiceController(AppOpsController appOpsController,
-            @Main Handler mainHandler) {
+    public ForegroundServiceController(NotificationEntryManager entryManager,
+            AppOpsController appOpsController, @Main Handler mainHandler) {
+        mEntryManager = entryManager;
         mMainHandler = mainHandler;
         appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> {
             mMainHandler.post(() -> {
@@ -81,6 +87,19 @@
     }
 
     /**
+     * Returns the keys for notifications from this package using the standard template,
+     * if they exist.
+     */
+    @Nullable
+    public ArraySet<String> getStandardLayoutKeys(int userId, String pkg) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) return null;
+            return services.getStandardLayoutKeys(pkg);
+        }
+    }
+
+    /**
      * Gets active app ops for this user and package
      */
     @Nullable
@@ -121,6 +140,31 @@
                 userServices.removeOp(packageName, appOpCode);
             }
         }
+
+        // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+        //  AppOpsCoordinator
+        // Update appOps if there are associated pending or visible notifications
+        final Set<String> notificationKeys = getStandardLayoutKeys(userId, packageName);
+        if (notificationKeys != null) {
+            boolean changed = false;
+            for (String key : notificationKeys) {
+                final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key);
+                if (entry != null
+                        && uid == entry.getSbn().getUid()
+                        && packageName.equals(entry.getSbn().getPackageName())) {
+                    synchronized (entry.mActiveAppOps) {
+                        if (active) {
+                            changed |= entry.mActiveAppOps.add(appOpCode);
+                        } else {
+                            changed |= entry.mActiveAppOps.remove(appOpCode);
+                        }
+                    }
+                }
+            }
+            if (changed) {
+                mEntryManager.updateNotifications("appOpChanged pkg=" + packageName);
+            }
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 1515272..bb44583 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -172,8 +172,24 @@
                                     sbn.getPackageName(), sbn.getKey());
                         }
                     }
+                    tagAppOps(entry);
                     return true;
                 },
                 true /* create if not found */);
     }
+
+    // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+    //  AppOpsCoordinator
+    private void tagAppOps(NotificationEntry entry) {
+        final StatusBarNotification sbn = entry.getSbn();
+        ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
+                sbn.getUserId(),
+                sbn.getPackageName());
+        synchronized (entry.mActiveAppOps) {
+            entry.mActiveAppOps.clear();
+            if (activeOps != null) {
+                entry.mActiveAppOps.addAll(activeOps);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 143f849..ae7f66b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -19,8 +19,12 @@
 import android.content.Context
 import android.media.MediaRouter2Manager
 import android.media.session.MediaController
+import androidx.annotation.AnyThread
+import androidx.annotation.MainThread
+import androidx.annotation.WorkerThread
 import com.android.settingslib.media.LocalMediaManager
 import com.android.settingslib.media.MediaDevice
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.Dumpable
 import com.android.systemui.dump.DumpManager
@@ -39,11 +43,12 @@
     private val localMediaManagerFactory: LocalMediaManagerFactory,
     private val mr2manager: MediaRouter2Manager,
     @Main private val fgExecutor: Executor,
+    @Background private val bgExecutor: Executor,
     private val mediaDataManager: MediaDataManager,
     private val dumpManager: DumpManager
 ) : MediaDataManager.Listener, Dumpable {
     private val listeners: MutableSet<Listener> = mutableSetOf()
-    private val entries: MutableMap<String, Token> = mutableMapOf()
+    private val entries: MutableMap<String, Entry> = mutableMapOf()
 
     init {
         mediaDataManager.addListener(this)
@@ -71,7 +76,7 @@
             val controller = data.token?.let {
                 MediaController(context, it)
             }
-            entry = Token(key, oldKey, controller,
+            entry = Entry(key, oldKey, controller,
                     localMediaManagerFactory.create(data.packageName))
             entries[key] = entry
             entry.start()
@@ -99,6 +104,7 @@
         }
     }
 
+    @MainThread
     private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) {
         val enabled = device != null
         val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
@@ -114,12 +120,13 @@
         fun onKeyRemoved(key: String)
     }
 
-    private inner class Token(
+    private inner class Entry(
         val key: String,
         val oldKey: String?,
         val controller: MediaController?,
         val localMediaManager: LocalMediaManager
     ) : LocalMediaManager.DeviceCallback {
+
         val token
             get() = controller?.sessionToken
         private var started = false
@@ -127,20 +134,27 @@
             set(value) {
                 if (!started || value != field) {
                     field = value
-                    processDevice(key, oldKey, value)
+                    fgExecutor.execute {
+                        processDevice(key, oldKey, value)
+                    }
                 }
             }
-        fun start() {
+
+        @AnyThread
+        fun start() = bgExecutor.execute {
             localMediaManager.registerCallback(this)
             localMediaManager.startScan()
             updateCurrent()
             started = true
         }
-        fun stop() {
+
+        @AnyThread
+        fun stop() = bgExecutor.execute {
             started = false
             localMediaManager.stopScan()
             localMediaManager.unregisterCallback(this)
         }
+
         fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
             val route = controller?.let {
                 mr2manager.getRoutingSessionForMediaController(it)
@@ -152,14 +166,18 @@
                 println("    route=$route")
             }
         }
-        override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute {
+
+        override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute {
             updateCurrent()
         }
+
         override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) {
-            fgExecutor.execute {
+            bgExecutor.execute {
                 updateCurrent()
             }
         }
+
+        @WorkerThread
         private fun updateCurrent() {
             val device = localMediaManager.getCurrentConnectedDevice()
             controller?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
index 5e2cd9c..9702035 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
@@ -17,6 +17,7 @@
 package com.android.systemui.pip;
 
 import android.app.TaskInfo;
+import android.content.pm.PackageManager;
 
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
@@ -31,27 +32,49 @@
 @Singleton
 public class PipUiEventLogger {
 
-    private final UiEventLogger mUiEventLogger;
+    private static final int INVALID_PACKAGE_UID = -1;
 
-    private TaskInfo mTaskInfo;
+    private final UiEventLogger mUiEventLogger;
+    private final PackageManager mPackageManager;
+
+    private String mPackageName;
+    private int mPackageUid = INVALID_PACKAGE_UID;
 
     @Inject
-    public PipUiEventLogger(UiEventLogger uiEventLogger) {
+    public PipUiEventLogger(UiEventLogger uiEventLogger, PackageManager packageManager) {
         mUiEventLogger = uiEventLogger;
+        mPackageManager = packageManager;
     }
 
     public void setTaskInfo(TaskInfo taskInfo) {
-        mTaskInfo = taskInfo;
+        if (taskInfo == null) {
+            mPackageName = null;
+            mPackageUid = INVALID_PACKAGE_UID;
+        } else {
+            mPackageName = taskInfo.topActivity.getPackageName();
+            mPackageUid = getUid(mPackageName, taskInfo.userId);
+        }
     }
 
     /**
      * Sends log via UiEvent, reference go/uievent for how to debug locally
      */
     public void log(PipUiEventEnum event) {
-        if (mTaskInfo == null) {
+        if (mPackageName == null || mPackageUid == INVALID_PACKAGE_UID) {
             return;
         }
-        mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName());
+        mUiEventLogger.log(event, mPackageUid, mPackageName);
+    }
+
+    private int getUid(String packageName, int userId) {
+        int uid = INVALID_PACKAGE_UID;
+        try {
+            uid = mPackageManager.getApplicationInfoAsUser(
+                    packageName, 0 /* ApplicationInfoFlags */, userId).uid;
+        } catch (PackageManager.NameNotFoundException e) {
+            // do nothing.
+        }
+        return uid;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 59118bf..a46ecaf 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -72,6 +72,8 @@
         private const val ALL_INDICATORS =
                 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+        private const val DEFAULT_ALL_INDICATORS = false
+        private const val DEFAULT_MIC_CAMERA = true
     }
 
     @VisibleForTesting
@@ -81,12 +83,12 @@
 
     fun isAllIndicatorsEnabled(): Boolean {
         return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                ALL_INDICATORS, false)
+                ALL_INDICATORS, DEFAULT_ALL_INDICATORS)
     }
 
     private fun isMicCameraEnabled(): Boolean {
         return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                MIC_CAMERA, false)
+                MIC_CAMERA, DEFAULT_MIC_CAMERA)
     }
 
     private var currentUserIds = emptyList<Int>()
@@ -118,12 +120,13 @@
 
                     // Running on the ui executor so can iterate on callbacks
                     if (properties.keyset.contains(ALL_INDICATORS)) {
-                        allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false)
+                        allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS,
+                                DEFAULT_ALL_INDICATORS)
                         callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
                     }
 
                     if (properties.keyset.contains(MIC_CAMERA)) {
-                        micCameraAvailable = properties.getBoolean(MIC_CAMERA, false)
+                        micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
                         callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
                     }
                     internalUiExecutor.updateListeningState()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 6b8afff..5bee9a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -486,6 +486,7 @@
                 }
             }
 
+            row.showAppOpsIcons(entry.mActiveAppOps);
             row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index 84108b1..4b244bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -76,9 +76,14 @@
         // extend the lifetime of foreground notification services to show for at least 5 seconds
         mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
 
+        // listen for new notifications to add appOps
+        mNotifPipeline.addCollectionListener(mNotifCollectionListener);
+
         // filter out foreground service notifications that aren't necessary anymore
         mNotifPipeline.addPreGroupFilter(mNotifFilter);
 
+        // when appOps change, update any relevant notifications to update appOps for
+        mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
     }
 
     /**
@@ -169,4 +174,82 @@
             }
         }
     };
+
+    /**
+     * Adds appOps to incoming and updating notifications
+     */
+    private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
+        @Override
+        public void onEntryAdded(NotificationEntry entry) {
+            tagAppOps(entry);
+        }
+
+        @Override
+        public void onEntryUpdated(NotificationEntry entry) {
+            tagAppOps(entry);
+        }
+
+        private void tagAppOps(NotificationEntry entry) {
+            final StatusBarNotification sbn = entry.getSbn();
+            // note: requires that the ForegroundServiceController is updating their appOps first
+            ArraySet<Integer> activeOps =
+                    mForegroundServiceController.getAppOps(
+                            sbn.getUser().getIdentifier(),
+                            sbn.getPackageName());
+
+            entry.mActiveAppOps.clear();
+            if (activeOps != null) {
+                entry.mActiveAppOps.addAll(activeOps);
+            }
+        }
+    };
+
+    private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
+        mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active));
+    }
+
+    /**
+     * Update the appOp for the posted notification associated with the current foreground service
+     *
+     * @param code code for appOp to add/remove
+     * @param uid of user the notification is sent to
+     * @param packageName package that created the notification
+     * @param active whether the appOpCode is active or not
+     */
+    private void handleAppOpsChanged(int code, int uid, String packageName, boolean active) {
+        Assert.isMainThread();
+
+        int userId = UserHandle.getUserId(uid);
+
+        // Update appOps of the app's posted notifications with standard layouts
+        final ArraySet<String> notifKeys =
+                mForegroundServiceController.getStandardLayoutKeys(userId, packageName);
+        if (notifKeys != null) {
+            boolean changed = false;
+            for (int i = 0; i < notifKeys.size(); i++) {
+                final NotificationEntry entry = findNotificationEntryWithKey(notifKeys.valueAt(i));
+                if (entry != null
+                        && uid == entry.getSbn().getUid()
+                        && packageName.equals(entry.getSbn().getPackageName())) {
+                    if (active) {
+                        changed |= entry.mActiveAppOps.add(code);
+                    } else {
+                        changed |= entry.mActiveAppOps.remove(code);
+                    }
+                }
+            }
+            if (changed) {
+                mNotifFilter.invalidateList();
+            }
+        }
+    }
+
+    private NotificationEntry findNotificationEntryWithKey(String key) {
+        for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) {
+            if (entry.getKey().equals(key)) {
+                return entry;
+            }
+        }
+        return null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
new file mode 100644
index 0000000..28c53dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
@@ -0,0 +1,214 @@
+/*
+ * 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 com.android.systemui.statusbar.notification.row;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+
+/**
+ * The guts of a notification revealed when performing a long press.
+ */
+public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent {
+    private static final String TAG = "AppOpsGuts";
+
+    private PackageManager mPm;
+
+    private String mPkg;
+    private String mAppName;
+    private int mAppUid;
+    private StatusBarNotification mSbn;
+    private ArraySet<Integer> mAppOps;
+    private MetricsLogger mMetricsLogger;
+    private OnSettingsClickListener mOnSettingsClickListener;
+    private NotificationGuts mGutsContainer;
+    private UiEventLogger mUiEventLogger;
+
+    private OnClickListener mOnOk = v -> {
+        mGutsContainer.closeControls(v, false);
+    };
+
+    public AppOpsInfo(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public interface OnSettingsClickListener {
+        void onClick(View v, String pkg, int uid, ArraySet<Integer> ops);
+    }
+
+    public void bindGuts(final PackageManager pm,
+            final OnSettingsClickListener onSettingsClick,
+            final StatusBarNotification sbn,
+            final UiEventLogger uiEventLogger,
+            ArraySet<Integer> activeOps) {
+        mPkg = sbn.getPackageName();
+        mSbn = sbn;
+        mPm = pm;
+        mAppName = mPkg;
+        mOnSettingsClickListener = onSettingsClick;
+        mAppOps = activeOps;
+        mUiEventLogger = uiEventLogger;
+
+        bindHeader();
+        bindPrompt();
+        bindButtons();
+
+        logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN);
+        mMetricsLogger = new MetricsLogger();
+        mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true);
+    }
+
+    private void bindHeader() {
+        // Package name
+        Drawable pkgicon = null;
+        ApplicationInfo info;
+        try {
+            info = mPm.getApplicationInfo(mPkg,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+            if (info != null) {
+                mAppUid = mSbn.getUid();
+                mAppName = String.valueOf(mPm.getApplicationLabel(info));
+                pkgicon = mPm.getApplicationIcon(info);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // app is gone, just show package name and generic icon
+            pkgicon = mPm.getDefaultActivityIcon();
+        }
+        ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
+        ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
+    }
+
+    private void bindPrompt() {
+        final TextView prompt = findViewById(R.id.prompt);
+        prompt.setText(getPrompt());
+    }
+
+    private void bindButtons() {
+        View settings =  findViewById(R.id.settings);
+        settings.setOnClickListener((View view) -> {
+            mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps);
+        });
+        TextView ok = findViewById(R.id.ok);
+        ok.setOnClickListener(mOnOk);
+        ok.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
+    }
+
+    private String getPrompt() {
+        if (mAppOps == null || mAppOps.size() == 0) {
+            return "";
+        } else if (mAppOps.size() == 1) {
+            if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
+                return mContext.getString(R.string.appops_camera);
+            } else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
+                return mContext.getString(R.string.appops_microphone);
+            } else {
+                return mContext.getString(R.string.appops_overlay);
+            }
+        } else if (mAppOps.size() == 2) {
+            if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
+                if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
+                    return mContext.getString(R.string.appops_camera_mic);
+                } else {
+                    return mContext.getString(R.string.appops_camera_overlay);
+                }
+            } else {
+                return mContext.getString(R.string.appops_mic_overlay);
+            }
+        } else {
+            return mContext.getString(R.string.appops_camera_mic_overlay);
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        if (mGutsContainer != null &&
+                event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            if (mGutsContainer.isExposed()) {
+                event.getText().add(mContext.getString(
+                        R.string.notification_channel_controls_opened_accessibility, mAppName));
+            } else {
+                event.getText().add(mContext.getString(
+                        R.string.notification_channel_controls_closed_accessibility, mAppName));
+            }
+        }
+    }
+
+    @Override
+    public void setGutsParent(NotificationGuts guts) {
+        mGutsContainer = guts;
+    }
+
+    @Override
+    public boolean willBeRemoved() {
+        return false;
+    }
+
+    @Override
+    public boolean shouldBeSaved() {
+        return false;
+    }
+
+    @Override
+    public boolean needsFalsingProtection() {
+        return false;
+    }
+
+    @Override
+    public View getContentView() {
+        return this;
+    }
+
+    @Override
+    public boolean handleCloseControls(boolean save, boolean force) {
+        logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_CLOSE);
+        if (mMetricsLogger != null) {
+            mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
+        }
+        return false;
+    }
+
+    @Override
+    public int getActualHeight() {
+        return getHeight();
+    }
+
+    private void logUiEvent(NotificationAppOpsEvent event) {
+        if (mSbn != null) {
+            mUiEventLogger.logWithInstanceId(event,
+                    mSbn.getUid(), mSbn.getPackageName(), mSbn.getInstanceId());
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 8ead7bf..94e12e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -239,6 +239,7 @@
     private boolean mShowNoBackground;
     private ExpandableNotificationRow mNotificationParent;
     private OnExpandClickListener mOnExpandClickListener;
+    private View.OnClickListener mOnAppOpsClickListener;
 
     // Listener will be called when receiving a long click event.
     // Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -1142,6 +1143,7 @@
             items.add(NotificationMenuRow.createPartialConversationItem(mContext));
             items.add(NotificationMenuRow.createInfoItem(mContext));
             items.add(NotificationMenuRow.createSnoozeItem(mContext));
+            items.add(NotificationMenuRow.createAppOpsItem(mContext));
             mMenuRow.setMenuItems(items);
         }
         if (existed) {
@@ -1607,6 +1609,7 @@
             RowContentBindStage rowContentBindStage,
             OnExpandClickListener onExpandClickListener,
             NotificationMediaManager notificationMediaManager,
+            OnAppOpsClickListener onAppOpsClickListener,
             FalsingManager falsingManager,
             StatusBarStateController statusBarStateController,
             PeopleNotificationIdentifier peopleNotificationIdentifier) {
@@ -1626,6 +1629,7 @@
         mRowContentBindStage = rowContentBindStage;
         mOnExpandClickListener = onExpandClickListener;
         mMediaManager = notificationMediaManager;
+        setAppOpsOnClickListener(onAppOpsClickListener);
         mFalsingManager = falsingManager;
         mStatusbarStateController = statusBarStateController;
         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
@@ -1682,6 +1686,14 @@
         requestLayout();
     }
 
+    public void showAppOpsIcons(ArraySet<Integer> activeOps) {
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.showAppOpsIcons(activeOps);
+        }
+        mPrivateLayout.showAppOpsIcons(activeOps);
+        mPublicLayout.showAppOpsIcons(activeOps);
+    }
+
     /** Sets the last time the notification being displayed audibly alerted the user. */
     public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
         if (NotificationUtils.useNewInterruptionModel(mContext)) {
@@ -1710,6 +1722,24 @@
         mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
     }
 
+    public View.OnClickListener getAppOpsOnClickListener() {
+        return mOnAppOpsClickListener;
+    }
+
+    void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
+        mOnAppOpsClickListener = v -> {
+            createMenu();
+            NotificationMenuRowPlugin provider = getProvider();
+            if (provider == null) {
+                return;
+            }
+            MenuItem menuItem = provider.getAppOpsMenuItem(mContext);
+            if (menuItem != null) {
+                l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem);
+            }
+        };
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index b132caf..7a6109d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -63,6 +63,7 @@
 
     private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
             this::logNotificationExpansion;
+    private final ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private final NotificationGutsManager mNotificationGutsManager;
     private Runnable mOnDismissRunnable;
     private final FalsingManager mFalsingManager;
@@ -100,6 +101,7 @@
         mStatusBarStateController = statusBarStateController;
         mNotificationGutsManager = notificationGutsManager;
         mOnDismissRunnable = onDismissRunnable;
+        mOnAppOpsClickListener = mNotificationGutsManager::openGuts;
         mAllowLongPress = allowLongPress;
         mFalsingManager = falsingManager;
         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
@@ -120,6 +122,7 @@
                 mRowContentBindStage,
                 mOnExpandClickListener,
                 mMediaManager,
+                mOnAppOpsClickListener,
                 mFalsingManager,
                 mStatusBarStateController,
                 mPeopleNotificationIdentifier
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 41a9b18..1f5b063 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1567,6 +1567,18 @@
         return header;
     }
 
+    public void showAppOpsIcons(ArraySet<Integer> activeOps) {
+        if (mContractedChild != null) {
+            mContractedWrapper.showAppOpsIcons(activeOps);
+        }
+        if (mExpandedChild != null) {
+            mExpandedWrapper.showAppOpsIcons(activeOps);
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.showAppOpsIcons(activeOps);
+        }
+    }
+
     /** Sets whether the notification being displayed audibly alerted the user. */
     public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
         if (mContractedChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index f543db7..25a0ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -42,6 +42,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -539,12 +541,21 @@
                 && Settings.Global.getInt(mContext.getContentResolver(),
                         NOTIFICATION_BUBBLES, 0) == 1;
 
+        Drawable person =  mIconFactory.getBaseIconDrawable(mShortcutInfo);
+        if (person == null) {
+            person = mContext.getDrawable(R.drawable.ic_person).mutate();
+            TypedArray ta = mContext.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+            int colorAccent = ta.getColor(0, 0);
+            ta.recycle();
+            person.setTint(colorAccent);
+        }
+
         PriorityOnboardingDialogController controller = mBuilderProvider.get()
                 .setContext(mUserContext)
                 .setView(onboardingView)
                 .setIgnoresDnd(ignoreDnd)
                 .setShowsAsBubble(showAsBubble)
-                .setIcon(mIconFactory.getBaseIconDrawable(mShortcutInfo))
+                .setIcon(person)
                 .setBadge(mIconFactory.getAppBadge(
                         mPackageName, UserHandle.getUserId(mSbn.getUid())))
                 .setOnSettingsClick(mOnConversationSettingsClickListener)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 3eed18a..24883f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -264,6 +264,8 @@
         try {
             if (gutsView instanceof NotificationSnooze) {
                 initializeSnoozeView(row, (NotificationSnooze) gutsView);
+            } else if (gutsView instanceof AppOpsInfo) {
+                initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
             } else if (gutsView instanceof NotificationInfo) {
                 initializeNotificationInfo(row, (NotificationInfo) gutsView);
             } else if (gutsView instanceof NotificationConversationInfo) {
@@ -301,6 +303,36 @@
     }
 
     /**
+     * Sets up the {@link AppOpsInfo} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param appOpsInfoView view to set up/bind within {@code row}
+     */
+    private void initializeAppOpsInfo(
+            final ExpandableNotificationRow row,
+            AppOpsInfo appOpsInfoView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getEntry().getSbn();
+        UserHandle userHandle = sbn.getUser();
+        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+                userHandle.getIdentifier());
+
+        AppOpsInfo.OnSettingsClickListener onSettingsClick =
+                (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
+                    mUiEventLogger.logWithInstanceId(
+                            NotificationAppOpsEvent.NOTIFICATION_APP_OPS_SETTINGS_CLICK,
+                            sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId());
+                    mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
+                    guts.resetFalsingCheck();
+                    startAppOpsSettingsActivity(pkg, uid, ops, row);
+        };
+        if (!row.getEntry().mActiveAppOps.isEmpty()) {
+            appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, mUiEventLogger,
+                    row.getEntry().mActiveAppOps);
+        }
+    }
+
+    /**
      * Sets up the {@link NotificationInfo} inside the notification row's guts.
      * @param row view to set up the guts for
      * @param notificationInfoView view to set up/bind within {@code row}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index a167925..5e1e3b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -76,6 +76,7 @@
     private Context mContext;
     private FrameLayout mMenuContainer;
     private NotificationMenuItem mInfoItem;
+    private MenuItem mAppOpsItem;
     private MenuItem mSnoozeItem;
     private ArrayList<MenuItem> mLeftMenuItems;
     private ArrayList<MenuItem> mRightMenuItems;
@@ -137,6 +138,11 @@
     }
 
     @Override
+    public MenuItem getAppOpsMenuItem(Context context) {
+        return mAppOpsItem;
+    }
+
+    @Override
     public MenuItem getSnoozeMenuItem(Context context) {
         return mSnoozeItem;
     }
@@ -258,6 +264,7 @@
             // Only show snooze for non-foreground notifications, and if the setting is on
             mSnoozeItem = createSnoozeItem(mContext);
         }
+        mAppOpsItem = createAppOpsItem(mContext);
         NotificationEntry entry = mParent.getEntry();
         int personNotifType = mPeopleNotificationIdentifier
                 .getPeopleNotificationType(entry.getSbn(), entry.getRanking());
@@ -273,6 +280,7 @@
             mRightMenuItems.add(mSnoozeItem);
         }
         mRightMenuItems.add(mInfoItem);
+        mRightMenuItems.add(mAppOpsItem);
         mLeftMenuItems.addAll(mRightMenuItems);
 
         populateMenuViews();
@@ -680,6 +688,14 @@
                 R.drawable.ic_settings);
     }
 
+    static MenuItem createAppOpsItem(Context context) {
+        AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate(
+                R.layout.app_ops_info, null, false);
+        MenuItem info = new NotificationMenuItem(context, null, appOpsContent,
+                -1 /*don't show in slow swipe menu */);
+        return info;
+    }
+
     private void addMenuView(MenuItem item, ViewGroup parent) {
         View menuView = item.getMenuView();
         if (menuView != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 86fc352..c747a7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
 
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.content.Context;
 import android.util.ArraySet;
@@ -63,6 +64,10 @@
     private TextView mHeaderText;
     private TextView mAppNameText;
     private ImageView mWorkProfileImage;
+    private View mCameraIcon;
+    private View mMicIcon;
+    private View mOverlayIcon;
+    private View mAppOps;
     private View mAudiblyAlertedIcon;
     private FrameLayout mIconContainer;
 
@@ -103,6 +108,7 @@
                     }
                 }, TRANSFORMING_VIEW_TITLE);
         resolveHeaderViews();
+        addAppOpsOnClickListener(row);
     }
 
     protected void resolveHeaderViews() {
@@ -113,6 +119,10 @@
         mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
         mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
         mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
+        mCameraIcon = mView.findViewById(com.android.internal.R.id.camera);
+        mMicIcon = mView.findViewById(com.android.internal.R.id.mic);
+        mOverlayIcon = mView.findViewById(com.android.internal.R.id.overlay);
+        mAppOps = mView.findViewById(com.android.internal.R.id.app_ops);
         mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon);
         if (mNotificationHeader != null) {
             mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
@@ -120,6 +130,38 @@
         }
     }
 
+    private void addAppOpsOnClickListener(ExpandableNotificationRow row) {
+        View.OnClickListener listener = row.getAppOpsOnClickListener();
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setAppOpsOnClickListener(listener);
+        }
+        if (mAppOps != null) {
+            mAppOps.setOnClickListener(listener);
+        }
+    }
+
+    /**
+     * Shows or hides 'app op in use' icons based on app usage.
+     */
+    @Override
+    public void showAppOpsIcons(ArraySet<Integer> appOps) {
+        if (appOps == null) {
+            return;
+        }
+        if (mOverlayIcon != null) {
+            mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+                    ? View.VISIBLE : View.GONE);
+        }
+        if (mCameraIcon != null) {
+            mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA)
+                    ? View.VISIBLE : View.GONE);
+        }
+        if (mMicIcon != null) {
+            mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO)
+                    ? View.VISIBLE : View.GONE);
+        }
+    }
+
     @Override
     public void onContentUpdated(ExpandableNotificationRow row) {
         super.onContentUpdated(row);
@@ -243,6 +285,15 @@
             mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
                     mHeaderText);
         }
+        if (mCameraIcon != null) {
+            mTransformationHelper.addViewTransformingToSimilar(mCameraIcon);
+        }
+        if (mMicIcon != null) {
+            mTransformationHelper.addViewTransformingToSimilar(mMicIcon);
+        }
+        if (mOverlayIcon != null) {
+            mTransformationHelper.addViewTransformingToSimilar(mOverlayIcon);
+        }
         if (mAudiblyAlertedIcon != null) {
             mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 4bf2794..30080e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -96,6 +96,14 @@
     public void onContentUpdated(ExpandableNotificationRow row) {
     }
 
+    /**
+     * Show a set of app opp icons in the layout.
+     *
+     * @param appOps which app ops to show
+     */
+    public void showAppOpsIcons(ArraySet<Integer> appOps) {
+    }
+
     public void onReinflated() {
         if (shouldClearBackgroundOnReapply()) {
             mBackgroundColor = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 5edcde1..99691b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1302,6 +1302,20 @@
         mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
     }
 
+    /**
+     * Show a set of app opp icons in the layout.
+     *
+     * @param appOps which app ops to show
+     */
+    public void showAppOpsIcons(ArraySet<Integer> appOps) {
+        if (mNotificationHeaderWrapper != null) {
+            mNotificationHeaderWrapper.showAppOpsIcons(appOps);
+        }
+        if (mNotificationHeaderWrapperLowPriority != null) {
+            mNotificationHeaderWrapperLowPriority.showAppOpsIcons(appOps);
+        }
+    }
+
     public void setRecentlyAudiblyAlerted(boolean audiblyAlertedRecently) {
         if (mNotificationHeaderWrapper != null) {
             mNotificationHeaderWrapper.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index e967a5d..60f0cd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -82,7 +82,8 @@
         allowTestableLooperAsMainThread();
 
         MockitoAnnotations.initMocks(this);
-        mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler);
+        mFsc = new ForegroundServiceController(
+                mEntryManager, mAppOpsController, mMainHandler);
         mListener = new ForegroundServiceNotificationListener(
                 mContext, mFsc, mEntryManager, mNotifPipeline,
                 mock(ForegroundServiceLifetimeExtender.class), mClock);
@@ -114,6 +115,85 @@
     }
 
     @Test
+    public void testAppOps_appOpChangedBeforeNotificationExists() {
+        // GIVEN app op exists, but notification doesn't exist in NEM yet
+        NotificationEntry entry = createFgEntry();
+        mFsc.onAppOpChanged(
+                AppOpsManager.OP_CAMERA,
+                entry.getSbn().getUid(),
+                entry.getSbn().getPackageName(),
+                true);
+        assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+        // WHEN the notification is added
+        mEntryListener.onPendingEntryAdded(entry);
+
+        // THEN the app op is added to the entry
+        Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+    }
+
+    @Test
+    public void testAppOps_appOpAddedToForegroundNotif() {
+        // GIVEN a notification associated with a foreground service
+        NotificationEntry entry = addFgEntry();
+        when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
+
+        // WHEN we are notified of a new app op for this notification
+        mFsc.onAppOpChanged(
+                AppOpsManager.OP_CAMERA,
+                entry.getSbn().getUid(),
+                entry.getSbn().getPackageName(),
+                true);
+
+        // THEN the app op is added to the entry
+        Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+        // THEN notification views are updated since the notification is visible
+        verify(mEntryManager, times(1)).updateNotifications(anyString());
+    }
+
+    @Test
+    public void testAppOpsAlreadyAdded() {
+        // GIVEN a foreground service associated notification that already has the correct app op
+        NotificationEntry entry = addFgEntry();
+        entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA);
+        when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
+
+        // WHEN we are notified of the same app op for this notification
+        mFsc.onAppOpChanged(
+                AppOpsManager.OP_CAMERA,
+                entry.getSbn().getUid(),
+                entry.getSbn().getPackageName(),
+                true);
+
+        // THEN the app op still exists in the notification entry
+        Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+        // THEN notification views aren't updated since nothing changed
+        verify(mEntryManager, never()).updateNotifications(anyString());
+    }
+
+    @Test
+    public void testAppOps_appOpNotAddedToUnrelatedNotif() {
+        // GIVEN no notification entries correspond to the newly updated appOp
+        NotificationEntry entry = addFgEntry();
+        when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null);
+
+        // WHEN a new app op is detected
+        mFsc.onAppOpChanged(
+                AppOpsManager.OP_CAMERA,
+                entry.getSbn().getUid(),
+                entry.getSbn().getPackageName(),
+                true);
+
+        // THEN we won't see appOps on the entry
+        Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+        // THEN notification views aren't updated since nothing changed
+        verify(mEntryManager, never()).updateNotifications(anyString());
+    }
+
+    @Test
     public void testAppOpsCRUD() {
         // no crash on remove that doesn't exist
         mFsc.onAppOpChanged(9, 1000, "pkg1", false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 3c6e19f..7bc15dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -72,7 +72,8 @@
     @Mock private lateinit var lmmFactory: LocalMediaManagerFactory
     @Mock private lateinit var lmm: LocalMediaManager
     @Mock private lateinit var mr2: MediaRouter2Manager
-    private lateinit var fakeExecutor: FakeExecutor
+    private lateinit var fakeFgExecutor: FakeExecutor
+    private lateinit var fakeBgExecutor: FakeExecutor
     @Mock private lateinit var dumpster: DumpManager
     @Mock private lateinit var listener: MediaDeviceManager.Listener
     @Mock private lateinit var device: MediaDevice
@@ -87,9 +88,10 @@
 
     @Before
     fun setUp() {
-        fakeExecutor = FakeExecutor(FakeSystemClock())
-        manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager,
-                dumpster)
+        fakeFgExecutor = FakeExecutor(FakeSystemClock())
+        fakeBgExecutor = FakeExecutor(FakeSystemClock())
+        manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor,
+                mediaDataManager, dumpster)
         manager.addListener(listener)
 
         // Configure mocks.
@@ -144,13 +146,15 @@
     fun loadAndRemoveMediaData() {
         manager.onMediaDataLoaded(KEY, null, mediaData)
         manager.onMediaDataRemoved(KEY)
+        fakeBgExecutor.runAllReady()
         verify(lmm).unregisterCallback(any())
     }
 
     @Test
     fun loadMediaDataWithNullToken() {
         manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
-        fakeExecutor.runAllReady()
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isTrue()
         assertThat(data.name).isEqualTo(DEVICE_NAME)
@@ -163,6 +167,8 @@
         reset(listener)
         // WHEN data is loaded with a new key
         manager.onMediaDataLoaded(KEY, KEY_OLD, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         // THEN the listener for the old key should removed.
         verify(lmm).unregisterCallback(any())
         // AND a new device event emitted
@@ -186,6 +192,8 @@
     fun unknownOldKey() {
         val oldKey = "unknown"
         manager.onMediaDataLoaded(KEY, oldKey, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any())
     }
 
@@ -193,13 +201,16 @@
     fun updateToSessionTokenWithNullRoute() {
         // GIVEN that media data has been loaded with a null token
         manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+        reset(listener)
         // WHEN media data is loaded with a different token
         // AND that token results in a null route
-        reset(listener)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         // THEN the device should be disabled
-        fakeExecutor.runAllReady()
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isNull()
@@ -210,7 +221,8 @@
     fun deviceEventOnAddNotification() {
         // WHEN a notification is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
-        val deviceCallback = captureCallback()
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         // THEN the update is dispatched to the listener
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isTrue()
@@ -230,10 +242,12 @@
     @Test
     fun deviceListUpdate() {
         manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
         val deviceCallback = captureCallback()
         // WHEN the device list changes
         deviceCallback.onDeviceListUpdate(mutableListOf(device))
-        assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+        assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
+        assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
         // THEN the update is dispatched to the listener
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isTrue()
@@ -244,10 +258,12 @@
     @Test
     fun selectedDeviceStateChanged() {
         manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
         val deviceCallback = captureCallback()
         // WHEN the selected device changes state
         deviceCallback.onSelectedDeviceStateChanged(device, 1)
-        assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+        assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
+        assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
         // THEN the update is dispatched to the listener
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isTrue()
@@ -270,6 +286,8 @@
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         // WHEN a notification is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         // THEN the device is disabled
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
@@ -281,13 +299,16 @@
     fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() {
         // GIVEN a notif is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         reset(listener)
         // AND MR2Manager returns null for routing session
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         // WHEN the selected device changes state
         val deviceCallback = captureCallback()
         deviceCallback.onSelectedDeviceStateChanged(device, 1)
-        fakeExecutor.runAllReady()
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         // THEN the device is disabled
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
@@ -299,13 +320,16 @@
     fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() {
         // GIVEN a notif is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         reset(listener)
         // GIVEN that MR2Manager returns null for routing session
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         // WHEN the selected device changes state
         val deviceCallback = captureCallback()
         deviceCallback.onDeviceListUpdate(mutableListOf(device))
-        fakeExecutor.runAllReady()
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         // THEN the device is disabled
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index 4ba29e6..25fb7d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -96,22 +96,24 @@
     }
 
     @Test
-    fun testNotListeningByDefault() {
+    fun testNotListeningAllByDefault() {
         assertFalse(privacyItemController.allIndicatorsAvailable)
-        assertFalse(privacyItemController.micCameraAvailable)
+    }
 
-        verify(appOpsController, never()).addCallback(any(), any())
+    @Test
+    fun testMicCameraListeningByDefault() {
+        assertTrue(privacyItemController.micCameraAvailable)
     }
 
     @Test
     fun testMicCameraChanged() {
-        changeMicCamera(true)
+        changeMicCamera(false) // default is true
         executor.runAllReady()
 
-        verify(callback).onFlagMicCameraChanged(true)
+        verify(callback).onFlagMicCameraChanged(false)
         verify(callback, never()).onFlagAllChanged(anyBoolean())
 
-        assertTrue(privacyItemController.micCameraAvailable)
+        assertFalse(privacyItemController.micCameraAvailable)
         assertFalse(privacyItemController.allIndicatorsAvailable)
     }
 
@@ -124,20 +126,19 @@
         verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
 
         assertTrue(privacyItemController.allIndicatorsAvailable)
-        assertFalse(privacyItemController.micCameraAvailable)
     }
 
     @Test
     fun testBothChanged() {
         changeAll(true)
-        changeMicCamera(true)
+        changeMicCamera(false)
         executor.runAllReady()
 
         verify(callback, atLeastOnce()).onFlagAllChanged(true)
-        verify(callback, atLeastOnce()).onFlagMicCameraChanged(true)
+        verify(callback, atLeastOnce()).onFlagMicCameraChanged(false)
 
         assertTrue(privacyItemController.allIndicatorsAvailable)
-        assertTrue(privacyItemController.micCameraAvailable)
+        assertFalse(privacyItemController.micCameraAvailable)
     }
 
     @Test
@@ -157,18 +158,11 @@
     }
 
     @Test
-    fun testAll_listening() {
-        changeAll(true)
-        executor.runAllReady()
-
-        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
-    }
-
-    @Test
     fun testAllFalse_notListening() {
         changeAll(true)
         executor.runAllReady()
         changeAll(false)
+        changeMicCamera(false)
         executor.runAllReady()
 
         verify(appOpsController).removeCallback(any(), any())
@@ -176,8 +170,8 @@
 
     @Test
     fun testSomeListening_stillListening() {
+        // Mic and camera are true by default
         changeAll(true)
-        changeMicCamera(true)
         executor.runAllReady()
         changeAll(false)
         executor.runAllReady()
@@ -186,7 +180,8 @@
     }
 
     @Test
-    fun testAllDeleted_stopListening() {
+    fun testAllDeleted_micCameraFalse_stopListening() {
+        changeMicCamera(false)
         changeAll(true)
         executor.runAllReady()
         changeAll(null)
@@ -196,13 +191,13 @@
     }
 
     @Test
-    fun testMicDeleted_stopListening() {
+    fun testMicDeleted_stillListening() {
         changeMicCamera(true)
         executor.runAllReady()
         changeMicCamera(null)
         executor.runAllReady()
 
-        verify(appOpsController).removeCallback(any(), any())
+        verify(appOpsController, never()).removeCallback(any(), any())
     }
 
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 5c5df26..38e8823 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -272,6 +272,7 @@
     @Test
     fun testNotListeningWhenIndicatorsDisabled() {
         changeAll(false)
+        changeMicCamera(false)
         privacyItemController.addCallback(callback)
         executor.runAllReady()
         verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 80fa8cc..92a2c87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -211,6 +211,19 @@
     }
 
     @Test
+    public void testUpdateNotificationViews_appOps() throws Exception {
+        NotificationEntry entry0 = createEntry();
+        entry0.setRow(spy(entry0.getRow()));
+        when(mEntryManager.getVisibleNotifications()).thenReturn(
+                Lists.newArrayList(entry0));
+        mListContainer.addContainerView(entry0.getRow());
+
+        mViewHierarchyManager.updateNotificationViews();
+
+        verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
+    }
+
+    @Test
     public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
         // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()
         mMadeReentrantCall = false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index ae39035..314b191 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -72,6 +72,8 @@
     private Notification mNotification;
     private AppOpsCoordinator mAppOpsCoordinator;
     private NotifFilter mForegroundFilter;
+    private NotifCollectionListener mNotifCollectionListener;
+    private AppOpsController.Callback mAppOpsCallback;
     private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
 
     private FakeSystemClock mClock = new FakeSystemClock();
@@ -108,6 +110,18 @@
                 lifetimeExtenderCaptor.capture());
         mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
 
+        // capture notifCollectionListener
+        ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
+                ArgumentCaptor.forClass(NotifCollectionListener.class);
+        verify(mNotifPipeline, times(1)).addCollectionListener(
+                notifCollectionCaptor.capture());
+        mNotifCollectionListener = notifCollectionCaptor.getValue();
+
+        // capture app ops callback
+        ArgumentCaptor<AppOpsController.Callback> appOpsCaptor =
+                ArgumentCaptor.forClass(AppOpsController.Callback.class);
+        verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture());
+        mAppOpsCallback = appOpsCaptor.getValue();
     }
 
     @Test
@@ -201,4 +215,134 @@
         assertFalse(mForegroundNotifLifetimeExtender
                 .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
     }
+
+    @Test
+    public void testAppOpsUpdateOnlyAppliedToRelevantNotificationWithStandardLayout() {
+        // GIVEN three current notifications, two with the same key but from different users
+        NotificationEntry entry1 = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setPkg(TEST_PKG)
+                .setId(1)
+                .build();
+        NotificationEntry entry2 = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setPkg(TEST_PKG)
+                .setId(2)
+                .build();
+        NotificationEntry entry3_diffUser = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID + 1))
+                .setPkg(TEST_PKG)
+                .setId(2)
+                .build();
+        when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3_diffUser));
+
+        // GIVEN that only entry2 has a standard layout
+        when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG))
+                .thenReturn(new ArraySet<>(List.of(entry2.getKey())));
+
+        // WHEN a new app ops code comes in
+        mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
+        mExecutor.runAllReady();
+
+        // THEN entry2's app ops are updated, but no one else's are
+        assertEquals(
+                new ArraySet<>(),
+                entry1.mActiveAppOps);
+        assertEquals(
+                new ArraySet<>(List.of(47)),
+                entry2.mActiveAppOps);
+        assertEquals(
+                new ArraySet<>(),
+                entry3_diffUser.mActiveAppOps);
+    }
+
+    @Test
+    public void testAppOpsUpdateAppliedToAllNotificationsWithStandardLayouts() {
+        // GIVEN three notifications with standard layouts
+        NotificationEntry entry1 = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setPkg(TEST_PKG)
+                .setId(1)
+                .build();
+        NotificationEntry entry2 = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setPkg(TEST_PKG)
+                .setId(2)
+                .build();
+        NotificationEntry entry3 = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setPkg(TEST_PKG)
+                .setId(3)
+                .build();
+        when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3));
+        when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG))
+                .thenReturn(new ArraySet<>(List.of(entry1.getKey(), entry2.getKey(),
+                        entry3.getKey())));
+
+        // WHEN a new app ops code comes in
+        mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
+        mExecutor.runAllReady();
+
+        // THEN all entries get updated
+        assertEquals(
+                new ArraySet<>(List.of(47)),
+                entry1.mActiveAppOps);
+        assertEquals(
+                new ArraySet<>(List.of(47)),
+                entry2.mActiveAppOps);
+        assertEquals(
+                new ArraySet<>(List.of(47)),
+                entry3.mActiveAppOps);
+    }
+
+    @Test
+    public void testAppOpsAreRemoved() {
+        // GIVEN One notification which is associated with app ops
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setPkg(TEST_PKG)
+                .setId(2)
+                .build();
+        when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry));
+        when(mForegroundServiceController.getStandardLayoutKeys(0, TEST_PKG))
+                .thenReturn(new ArraySet<>(List.of(entry.getKey())));
+
+        // GIVEN that the notification's app ops are already [47, 33]
+        mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
+        mAppOpsCallback.onActiveStateChanged(33, NOTIF_USER_ID, TEST_PKG, true);
+        mExecutor.runAllReady();
+        assertEquals(
+                new ArraySet<>(List.of(47, 33)),
+                entry.mActiveAppOps);
+
+        // WHEN one of the app ops is removed
+        mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, false);
+        mExecutor.runAllReady();
+
+        // THEN the entry's active app ops are updated as well
+        assertEquals(
+                new ArraySet<>(List.of(33)),
+                entry.mActiveAppOps);
+    }
+
+    @Test
+    public void testNullAppOps() {
+        // GIVEN one notification with app ops
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setPkg(TEST_PKG)
+                .setId(2)
+                .build();
+        entry.mActiveAppOps.clear();
+        entry.mActiveAppOps.addAll(List.of(47, 33));
+
+        // WHEN the notification is updated and the foreground service controller returns null for
+        // this notification
+        when(mForegroundServiceController.getAppOps(entry.getSbn().getUser().getIdentifier(),
+                entry.getSbn().getPackageName())).thenReturn(null);
+        mNotifCollectionListener.onEntryUpdated(entry);
+
+        // THEN the entry's active app ops is updated to empty
+        assertTrue(entry.mActiveAppOps.isEmpty());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
new file mode 100644
index 0000000..43d8b50
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
@@ -0,0 +1,230 @@
+/*
+ * 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 com.android.systemui.statusbar.notification.row;
+
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@UiThreadTest
+public class AppOpsInfoTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test_package";
+    private static final int TEST_UID = 1;
+
+    private AppOpsInfo mAppOpsInfo;
+    private final PackageManager mMockPackageManager = mock(PackageManager.class);
+    private final NotificationGuts mGutsParent = mock(NotificationGuts.class);
+    private StatusBarNotification mSbn;
+    private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
+
+    @Before
+    public void setUp() throws Exception {
+        // Inflate the layout
+        final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+        mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null);
+        mAppOpsInfo.setGutsParent(mGutsParent);
+
+        // PackageManager must return a packageInfo and applicationInfo.
+        final PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = TEST_PACKAGE_NAME;
+        when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
+                .thenReturn(packageInfo);
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = TEST_UID;  // non-zero
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+                applicationInfo);
+
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+                new Notification(), UserHandle.CURRENT, null, 0);
+    }
+
+    @Test
+    public void testBindNotification_SetsTextApplicationName() {
+        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
+        final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname);
+        assertTrue(textView.getText().toString().contains("App Name"));
+    }
+
+    @Test
+    public void testBindNotification_SetsPackageIcon() {
+        final Drawable iconDrawable = mock(Drawable.class);
+        when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
+                .thenReturn(iconDrawable);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
+        final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon);
+        assertEquals(iconDrawable, iconView.getDrawable());
+    }
+
+    @Test
+    public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        final CountDownLatch latch = new CountDownLatch(1);
+        mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
+                ArraySet<Integer> ops) -> {
+            assertEquals(TEST_PACKAGE_NAME, pkg);
+            assertEquals(expectedOps, ops);
+            assertEquals(TEST_UID, uid);
+            latch.countDown();
+        }, mSbn, mUiEventLogger, expectedOps);
+
+        final View settingsButton = mAppOpsInfo.findViewById(R.id.settings);
+        settingsButton.performClick();
+        // Verify that listener was triggered.
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    public void testBindNotification_LogsOpen() throws Exception {
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
+        assertEquals(1, mUiEventLogger.numLogs());
+        assertEquals(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN.getId(),
+                mUiEventLogger.eventId(0));
+    }
+
+    @Test
+    public void testOk() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        final CountDownLatch latch = new CountDownLatch(1);
+        mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
+                ArraySet<Integer> ops) -> {
+            assertEquals(TEST_PACKAGE_NAME, pkg);
+            assertEquals(expectedOps, ops);
+            assertEquals(TEST_UID, uid);
+            latch.countDown();
+        }, mSbn, mUiEventLogger, expectedOps);
+
+        final View okButton = mAppOpsInfo.findViewById(R.id.ok);
+        okButton.performClick();
+        assertEquals(1, latch.getCount());
+        verify(mGutsParent, times(1)).closeControls(eq(okButton), anyBoolean());
+    }
+
+    @Test
+    public void testPrompt_camera() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is using the camera.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_mic() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_RECORD_AUDIO);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is using the microphone.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_overlay() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is displaying over other apps on your screen.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_camera_mic() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        expectedOps.add(OP_RECORD_AUDIO);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is using the microphone and camera.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_camera_mic_overlay() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        expectedOps.add(OP_RECORD_AUDIO);
+        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is displaying over other apps on your screen and using"
+                + " the microphone and camera.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_camera_overlay() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is displaying over other apps on your screen and using"
+                + " the camera.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_mic_overlay() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_RECORD_AUDIO);
+        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is displaying over other apps on your screen and using"
+                + " the microphone.", prompt.getText());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 4758d23..2684cc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -35,10 +35,12 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AppOpsManager;
 import android.app.NotificationChannel;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArraySet;
 import android.view.View;
 
 import androidx.test.filters.SmallTest;
@@ -211,6 +213,46 @@
     }
 
     @Test
+    public void testShowAppOps_noHeader() {
+        // public notification is custom layout - no header
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setAppOpsOnClickListener(null);
+        mGroupRow.showAppOpsIcons(null);
+    }
+
+    @Test
+    public void testShowAppOpsIcons_header() {
+        NotificationContentView publicLayout = mock(NotificationContentView.class);
+        mGroupRow.setPublicLayout(publicLayout);
+        NotificationContentView privateLayout = mock(NotificationContentView.class);
+        mGroupRow.setPrivateLayout(privateLayout);
+        NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+        when(mockContainer.getNotificationChildCount()).thenReturn(1);
+        mGroupRow.setChildrenContainer(mockContainer);
+
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
+        mGroupRow.showAppOpsIcons(ops);
+
+        verify(mockContainer, times(1)).showAppOpsIcons(ops);
+        verify(privateLayout, times(1)).showAppOpsIcons(ops);
+        verify(publicLayout, times(1)).showAppOpsIcons(ops);
+
+    }
+
+    @Test
+    public void testAppOpsOnClick() {
+        ExpandableNotificationRow.OnAppOpsClickListener l = mock(
+                ExpandableNotificationRow.OnAppOpsClickListener.class);
+        View view = mock(View.class);
+
+        mGroupRow.setAppOpsOnClickListener(l);
+
+        mGroupRow.getAppOpsOnClickListener().onClick(view);
+        verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
+    }
+
+    @Test
     public void testHeadsUpAnimatingAwayListener() {
         mGroupRow.setHeadsUpAnimatingAway(true);
         Assert.assertEquals(true, mHeadsUpAnimatingAway);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index b02f274..ed4f8b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -76,6 +76,32 @@
 
     @Test
     @UiThreadTest
+    public void testShowAppOpsIcons() {
+        View mockContracted = mock(NotificationHeaderView.class);
+        when(mockContracted.findViewById(com.android.internal.R.id.mic))
+                .thenReturn(mockContracted);
+        View mockExpanded = mock(NotificationHeaderView.class);
+        when(mockExpanded.findViewById(com.android.internal.R.id.mic))
+                .thenReturn(mockExpanded);
+        View mockHeadsUp = mock(NotificationHeaderView.class);
+        when(mockHeadsUp.findViewById(com.android.internal.R.id.mic))
+                .thenReturn(mockHeadsUp);
+
+        mView.setContractedChild(mockContracted);
+        mView.setExpandedChild(mockExpanded);
+        mView.setHeadsUpChild(mockHeadsUp);
+
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(AppOpsManager.OP_RECORD_AUDIO);
+        mView.showAppOpsIcons(ops);
+
+        verify(mockContracted, times(1)).setVisibility(View.VISIBLE);
+        verify(mockExpanded, times(1)).setVisibility(View.VISIBLE);
+        verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    @UiThreadTest
     public void testExpandButtonFocusIsCalled() {
         View mockContractedEB = mock(NotificationExpandButton.class);
         View mockContracted = mock(NotificationHeaderView.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index d2ff2ad..0c6409b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -421,6 +421,7 @@
                 mBindStage,
                 mock(OnExpandClickListener.class),
                 mock(NotificationMediaManager.class),
+                mock(ExpandableNotificationRow.OnAppOpsClickListener.class),
                 mock(FalsingManager.class),
                 mStatusBarStateController,
                 mPeopleNotificationIdentifier);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e42dd35..5d0981d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6245,6 +6245,8 @@
                     for (NotificationRecord r : enqueued) {
                         if (r.mUpdateTimeMs > mWhen) {
                             // At least one enqueue was posted after the cancel, so we're invalid
+                            Slog.i(TAG, "notification cancel ignored due to newer enqueued entry"
+                                    + "key=" + r.getSbn().getKey());
                             return;
                         }
                     }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index cd508d0..959aedc 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1990,7 +1990,7 @@
         return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea());
     }
 
-    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+    void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,
             boolean newTask, boolean keepCurTransition, ActivityOptions options) {
         Task rTask = r.getTask();
         final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
@@ -3336,7 +3336,11 @@
         // Do not sleep activities in this stack if we're marked as focused and the keyguard
         // is in the process of going away.
         if (isFocusedStackOnDisplay()
-                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
+                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()
+                // Avoid resuming activities on secondary displays since we don't want bubble
+                // activities to be resumed while bubble is still collapsed.
+                // TODO(b/113840485): Having keyguard going away state for secondary displays.
+                && display.isDefaultDisplay) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b869eb5..a78232c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1697,8 +1697,9 @@
         mRootWindowContainer.sendPowerHintForLaunchStartIfNeeded(
                 false /* forceSend */, mStartActivity);
 
-        mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(),
-                newTask, mKeepCurTransition, mOptions);
+        mTargetStack.startActivityLocked(mStartActivity,
+                topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask,
+                mKeepCurTransition, mOptions);
         if (mDoResume) {
             final ActivityRecord topTaskActivity =
                     mStartActivity.getTask().topRunningActivityLocked();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 049e46f..f57c916 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2305,10 +2305,6 @@
 
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            if (display.shouldSleep()) {
-                continue;
-            }
-
             boolean resumedOnDisplay = false;
             for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
                 final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 1b42a04..e898c25 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1206,19 +1206,22 @@
     @Test
     public void testShouldSleepActivities() {
         // When focused activity and keyguard is going away, we should not sleep regardless
-        // of the display state
+        // of the display state, but keyguard-going-away should only take effects on default
+        // display since there is no keyguard on secondary displays (yet).
         verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
-                true /* displaySleeping */, false /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
+        verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+                true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */);
 
         // When not the focused stack, defer to display sleeping state.
         verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/,
-                true /* displaySleeping */, true /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
 
         // If keyguard is going away, defer to the display sleeping state.
         verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
-                true /* displaySleeping */, true /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
         verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
-                false /* displaySleeping */, false /* expected*/);
+                false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
     }
 
     @Test
@@ -1428,9 +1431,11 @@
     }
 
     private void verifyShouldSleepActivities(boolean focusedStack,
-            boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
+            boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
+            boolean expected) {
         final DisplayContent display = mock(DisplayContent.class);
         final KeyguardController keyguardController = mSupervisor.getKeyguardController();
+        display.isDefaultDisplay = isDefaultDisplay;
 
         doReturn(display).when(mStack).getDisplay();
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index c848736..51db099 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -896,24 +896,6 @@
         assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask());
     }
 
-    @Test
-    public void testResumeFocusedStackOnSleepingDisplay() {
-        // Create an activity on secondary display.
-        final TestDisplayContent secondDisplay = addNewDisplayContentAt(
-                DisplayContent.POSITION_TOP);
-        final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mService).setStack(stack).build();
-        spyOn(activity);
-        spyOn(stack);
-
-        // Cannot resumed activities on secondary display if the display should sleep.
-        doReturn(true).when(secondDisplay).shouldSleep();
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
-        verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any());
-        verify(activity, never()).makeActiveIfNeeded(any());
-    }
-
     /**
      * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
      * info for test cases.