Adds silence toggle to notification settings.

This change modifies the UI that appears when a notification is
long-pressed to include a third option when the notification could
potentially buzz or make sound, allowing the user to promote or demote
the notification's channel such that it will not alert (or will start
alerting) for future notifications.

Test: atest SystemUITests
Bug: 116622974
Change-Id: Ia070d0ef1b181b4de8b3d49ace9ff2a65fed8deb
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 56e6aea..64eae0c 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1462,6 +1462,7 @@
         private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
         private boolean mHidden;
         private boolean mAudiblyAlerted;
+        private boolean mNoisy;
         private ArrayList<Notification.Action> mSmartActions;
         private ArrayList<CharSequence> mSmartReplies;
 
@@ -1636,6 +1637,11 @@
             return mAudiblyAlerted;
         }
 
+        /** @hide */
+        public boolean isNoisy() {
+            return mNoisy;
+        }
+
         /**
          * @hide
          */
@@ -1646,7 +1652,8 @@
                 NotificationChannel channel, ArrayList<String> overridePeople,
                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
                 int userSentiment, boolean hidden, boolean audiblyAlerted,
-                ArrayList<Notification.Action> smartActions, ArrayList<CharSequence> smartReplies) {
+                boolean noisy, ArrayList<Notification.Action> smartActions,
+                ArrayList<CharSequence> smartReplies) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1663,6 +1670,7 @@
             mUserSentiment = userSentiment;
             mHidden = hidden;
             mAudiblyAlerted = audiblyAlerted;
+            mNoisy = noisy;
             mSmartActions = smartActions;
             mSmartReplies = smartReplies;
         }
@@ -1715,6 +1723,7 @@
         private ArrayMap<String, Integer> mUserSentiment;
         private ArrayMap<String, Boolean> mHidden;
         private ArrayMap<String, Boolean> mAudiblyAlerted;
+        private ArrayMap<String, Boolean> mNoisy;
         private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
         private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
 
@@ -1746,7 +1755,8 @@
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                     getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
                     getShowBadge(key), getUserSentiment(key), getHidden(key),
-                    getAudiblyAlerted(key), getSmartActions(key), getSmartReplies(key));
+                    getAudiblyAlerted(key), getNoisy(key), getSmartActions(key),
+                    getSmartReplies(key));
             return rank >= 0;
         }
 
@@ -1894,6 +1904,16 @@
             return audiblyAlerted == null ? false : audiblyAlerted.booleanValue();
         }
 
+        private boolean getNoisy(String key) {
+            synchronized (this) {
+                if (mNoisy == null) {
+                    buildNoisyLocked();
+                }
+            }
+            Boolean noisy = mNoisy.get(key);
+            return noisy == null ? false : noisy.booleanValue();
+        }
+
         private ArrayList<Notification.Action> getSmartActions(String key) {
             synchronized (this) {
                 if (mSmartActions == null) {
@@ -2034,6 +2054,11 @@
         }
 
         // Locked by 'this'
+        private void buildNoisyLocked() {
+            mNoisy = buildBooleanMapFromBundle(mRankingUpdate.getNoisy());
+        }
+
+        // Locked by 'this'
         private void buildSmartActions() {
             Bundle smartActions = mRankingUpdate.getSmartActions();
             mSmartActions = new ArrayMap<>(smartActions.size());
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index b561bfd..f80df93 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -40,13 +40,14 @@
     private final Bundle mSmartActions;
     private final Bundle mSmartReplies;
     private final Bundle mAudiblyAlerted;
+    private final Bundle mNoisy;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
             Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
             Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions,
-            Bundle smartReplies, Bundle audiblyAlerted) {
+            Bundle smartReplies, Bundle audiblyAlerted, Bundle noisy) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -63,6 +64,7 @@
         mSmartActions = smartActions;
         mSmartReplies = smartReplies;
         mAudiblyAlerted = audiblyAlerted;
+        mNoisy = noisy;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -83,6 +85,7 @@
         mSmartActions = in.readBundle();
         mSmartReplies = in.readBundle();
         mAudiblyAlerted = in.readBundle();
+        mNoisy = in.readBundle();
     }
 
     @Override
@@ -108,6 +111,7 @@
         out.writeBundle(mSmartActions);
         out.writeBundle(mSmartReplies);
         out.writeBundle(mAudiblyAlerted);
+        out.writeBundle(mNoisy);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -184,4 +188,8 @@
     public Bundle getAudiblyAlerted() {
         return mAudiblyAlerted;
     }
+
+    public Bundle getNoisy() {
+        return mNoisy;
+    }
 }
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index f138685..c86ebe7 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -152,6 +152,13 @@
                 android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
                 style="@style/TextAppearance.NotificationInfo.Button" />
             <TextView
+                android:id="@+id/toggle_silent"
+                android:text="@string/inline_silent_button_silent"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
+                style="@style/TextAppearance.NotificationInfo.Button" />
+            <TextView
                 android:id="@+id/keep"
                 android:minWidth="48dp"
                 android:text="@string/inline_keep_button"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6f5d657..50454fc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1540,6 +1540,12 @@
     <!-- Notification inline controls: Shown when a channel's notifications are minimized -->
     <string name="notification_channel_minimized">These notifications will be minimized</string>
 
+    <!-- Notification inline controls: Shown when a channel's notifications are silenced [CHAR_LIMIT=100] -->
+    <string name="notification_channel_silenced">These notifications will be shown silently</string>
+
+    <!-- Notification inline controls: Shown when a channel's notifications are set to alert [CHAR_LIMIT=100] -->
+    <string name="notification_channel_unsilenced">These notifications will alert you</string>
+
     <!-- Notification Inline controls: continue receiving notifications prompt, channel level -->
     <string name="inline_blocking_helper">You usually dismiss these notifications.
     \nKeep showing them?</string>
@@ -1556,6 +1562,12 @@
     <!-- Notification inline controls: minimize notifications button -->
     <string name="inline_minimize_button">Minimize</string>
 
+    <!-- Notification inline controls: show notifications silently button [CHAR_LIMIT=25] -->
+    <string name="inline_silent_button_silent">Show silently</string>
+
+    <!-- Notification inline controls: show and alert button [CHAR_LIMIT=25] -->
+    <string name="inline_silent_button_alert">Show and alert</string>
+
     <!-- Notification Inline controls: continue receiving notifications prompt, app level -->
     <string name="inline_keep_showing_app">Keep showing notifications from this app?</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index b84b77c..f7c07f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -102,6 +102,8 @@
         public StatusBarNotification notification;
         public NotificationChannel channel;
         public boolean audiblyAlerted;
+        public boolean noisy;
+        public int importance;
         public StatusBarIconView icon;
         public StatusBarIconView expandedIcon;
         public ExpandableNotificationRow row; // the outer expanded view
@@ -155,6 +157,8 @@
         public void populateFromRanking(@NonNull Ranking ranking) {
             channel = ranking.getChannel();
             audiblyAlerted = ranking.audiblyAlerted();
+            noisy = ranking.isNoisy();
+            importance = ranking.getImportance();
             snoozeCriteria = ranking.getSnoozeCriteria();
             userSentiment = ranking.getUserSentiment();
             smartActions = ranking.getSmartActions() == null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
index 28c07a3..43b5503 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
@@ -30,6 +30,9 @@
     /** Counter tag for when the user hits 'stop notifications' in the blocking helper. */
     public static final String BLOCKING_HELPER_STOP_NOTIFICATIONS =
             "blocking_helper_stop_notifications";
+    /** Counter tag for when the user hits 'show silently' in the blocking helper. */
+    public static final String BLOCKING_HELPER_TOGGLE_SILENT =
+            "blocking_helper_toggle_silent";
     /** Counter tag for when the user hits 'keep showing' in the blocking helper. */
     public static final String BLOCKING_HELPER_KEEP_SHOWING =
             "blocking_helper_keep_showing";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index 0a197da..fbe9c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -23,7 +23,6 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -31,6 +30,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
@@ -98,6 +99,11 @@
          * Return whether something changed and needs to be saved, possibly requiring a bouncer.
          */
         boolean shouldBeSaved();
+
+        /**
+         * Called when the guts view has finished its close animation.
+         */
+        default void onFinishedClosing() {}
     }
 
     public interface OnGutsClosedListener {
@@ -304,7 +310,7 @@
                         x, y, r, 0);
                 a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
                 a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-                a.addListener(new AnimateCloseListener(this /* view */));
+                a.addListener(new AnimateCloseListener(this /* view */, mGutsContent));
                 a.start();
             } else {
                 // Fade in the blocking helper.
@@ -312,11 +318,12 @@
                         .alpha(0f)
                         .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
                         .setInterpolator(Interpolators.ALPHA_OUT)
-                        .setListener(new AnimateCloseListener(this /* view */))
+                        .setListener(new AnimateCloseListener(this, /* view */mGutsContent))
                         .start();
             }
         } else {
             Log.w(TAG, "Failed to animate guts close");
+            mGutsContent.onFinishedClosing();
         }
     }
 
@@ -414,15 +421,18 @@
     /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
     private static class AnimateCloseListener extends AnimatorListenerAdapter {
         final View mView;
+        private final GutsContent mGutsContent;
 
-        private AnimateCloseListener(View view) {
+        private AnimateCloseListener(View view, GutsContent gutsContent) {
             mView = view;
+            mGutsContent = gutsContent;
         }
 
         @Override
         public void onAnimationEnd(Animator animation) {
             super.onAnimationEnd(animation);
             mView.setVisibility(View.GONE);
+            mGutsContent.onFinishedClosing();
         }
     }
 }
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 2499952..b838c9b 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
@@ -28,7 +28,6 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.Uri;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -46,15 +45,13 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -298,7 +295,9 @@
                 mDeviceProvisionedController.isDeviceProvisioned(),
                 row.getIsNonblockable(),
                 isForBlockingHelper,
-                row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
+                row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
+                row.getEntry().noisy,
+                row.getEntry().importance);
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 903c272..522da4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -16,13 +16,18 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -54,6 +59,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
 
 import java.util.List;
@@ -65,6 +71,17 @@
 public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
     private static final String TAG = "InfoGuts";
 
+    @IntDef(prefix = { "SWAP_CONTENT_" }, value = {
+            SWAP_CONTENT_UNDO,
+            SWAP_CONTENT_TOGGLE_SILENT,
+            SWAP_CONTENT_BLOCK,
+    })
+    @interface SwapContentAction {}
+
+    private static final int SWAP_CONTENT_UNDO = 0;
+    private static final int SWAP_CONTENT_TOGGLE_SILENT = 1;
+    private static final int SWAP_CONTENT_BLOCK = 2;
+
     private INotificationManager mINotificationManager;
     private PackageManager mPm;
     private MetricsLogger mMetricsLogger;
@@ -74,7 +91,8 @@
     private int mAppUid;
     private int mNumUniqueChannelsInRow;
     private NotificationChannel mSingleNotificationChannel;
-    private int mStartingUserImportance;
+    private int mStartingChannelImportance;
+    private int mStartingChannelOrNotificationImportance;
     private int mChosenImportance;
     private boolean mIsSingleDefaultChannel;
     private boolean mIsNonblockable;
@@ -82,6 +100,7 @@
     private AnimatorSet mExpandAnimation;
     private boolean mIsForeground;
     private boolean mIsDeviceProvisioned;
+    private boolean mIsNoisy;
 
     private CheckSaveListener mCheckSaveListener;
     private OnSettingsClickListener mOnSettingsClickListener;
@@ -102,10 +121,22 @@
         closeControls(v);
     };
 
+    private OnClickListener mOnToggleSilent = v -> {
+        Runnable saveImportance = () -> {
+            mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
+            swapContent(SWAP_CONTENT_TOGGLE_SILENT);
+        };
+        if (mCheckSaveListener != null) {
+            mCheckSaveListener.checkSave(saveImportance, mSbn);
+        } else {
+            saveImportance.run();
+        }
+    };
+
     private OnClickListener mOnStopOrMinimizeNotifications = v -> {
         Runnable saveImportance = () -> {
             mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
-            swapContent(false);
+            swapContent(SWAP_CONTENT_BLOCK);
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -118,7 +149,7 @@
         // Reset exit counter that we'll log and record an undo event separately (not an exit event)
         mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
         logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
-        swapContent(true);
+        swapContent(SWAP_CONTENT_UNDO);
     };
 
     public NotificationInfo(Context context, AttributeSet attrs) {
@@ -152,12 +183,15 @@
             final OnSettingsClickListener onSettingsClick,
             final OnAppSettingsClickListener onAppSettingsClick,
             boolean isDeviceProvisioned,
-            boolean isNonblockable)
+            boolean isNonblockable,
+            boolean isNoisy,
+            int importance)
             throws RemoteException {
         bindNotification(pm, iNotificationManager, pkg, notificationChannel,
                 numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
                 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
-                false /* isBlockingHelper */, false /* isUserSentimentNegative */);
+                false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy,
+                importance);
     }
 
     public void bindNotification(
@@ -173,7 +207,9 @@
             boolean isDeviceProvisioned,
             boolean isNonblockable,
             boolean isForBlockingHelper,
-            boolean isUserSentimentNegative)
+            boolean isUserSentimentNegative,
+            boolean isNoisy,
+            int importance)
             throws RemoteException {
         mINotificationManager = iNotificationManager;
         mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -186,7 +222,10 @@
         mCheckSaveListener = checkSaveListener;
         mOnSettingsClickListener = onSettingsClick;
         mSingleNotificationChannel = notificationChannel;
-        mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
+        int channelImportance = mSingleNotificationChannel.getImportance();
+        mStartingChannelImportance = mChosenImportance = channelImportance;
+        mStartingChannelOrNotificationImportance =
+                channelImportance == IMPORTANCE_UNSPECIFIED ? importance : channelImportance;
         mNegativeUserSentiment = isUserSentimentNegative;
         mIsNonblockable = isNonblockable;
         mIsForeground =
@@ -194,6 +233,7 @@
         mIsForBlockingHelper = isForBlockingHelper;
         mAppUid = mSbn.getUid();
         mIsDeviceProvisioned = isDeviceProvisioned;
+        mIsNoisy = isNoisy;
 
         int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
                 pkg, mAppUid, false /* includeDeleted */);
@@ -306,7 +346,8 @@
     }
 
     private boolean hasImportanceChanged() {
-        return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance;
+        return mSingleNotificationChannel != null
+                && mStartingChannelImportance != mChosenImportance;
     }
 
     private void saveImportance() {
@@ -320,34 +361,46 @@
      */
     private void updateImportance() {
         MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
-                mChosenImportance - mStartingUserImportance);
+                mChosenImportance - mStartingChannelImportance);
 
         Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
         bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
                 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
-                mStartingUserImportance, mChosenImportance));
+                mStartingChannelImportance, mChosenImportance));
     }
 
     private void bindButtons() {
         // Set up stay-in-notification actions
         View block =  findViewById(R.id.block);
         TextView keep = findViewById(R.id.keep);
+        TextView silent = findViewById(R.id.toggle_silent);
         View minimize = findViewById(R.id.minimize);
 
         findViewById(R.id.undo).setOnClickListener(mOnUndo);
         block.setOnClickListener(mOnStopOrMinimizeNotifications);
         keep.setOnClickListener(mOnKeepShowing);
+        silent.setOnClickListener(mOnToggleSilent);
         minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
 
         if (mIsNonblockable) {
             keep.setText(android.R.string.ok);
             block.setVisibility(GONE);
+            silent.setVisibility(GONE);
             minimize.setVisibility(GONE);
         } else if (mIsForeground) {
             block.setVisibility(GONE);
+            silent.setVisibility(GONE);
             minimize.setVisibility(VISIBLE);
-        } else if (!mIsForeground) {
+        } else {
             block.setVisibility(VISIBLE);
+            boolean showToggleSilent = mIsNoisy
+                    && NotificationUtils.useNewInterruptionModel(mContext);
+            silent.setVisibility(showToggleSilent ? VISIBLE : GONE);
+            boolean isCurrentlyAlerting =
+                    mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT;
+            silent.setText(isCurrentlyAlerting
+                    ? R.string.inline_silent_button_silent
+                    : R.string.inline_silent_button_alert);
             minimize.setVisibility(GONE);
         }
 
@@ -368,7 +421,7 @@
         }
     }
 
-    private void swapContent(boolean showPrompt) {
+    private void swapContent(@SwapContentAction int action) {
         if (mExpandAnimation != null) {
             mExpandAnimation.cancel();
         }
@@ -378,26 +431,43 @@
         TextView confirmationText = findViewById(R.id.confirmation_text);
         View header = findViewById(R.id.header);
 
-        if (showPrompt) {
-            mChosenImportance = mStartingUserImportance;
-        } else if (mIsForeground) {
-            mChosenImportance = IMPORTANCE_MIN;
-            confirmationText.setText(R.string.notification_channel_minimized);
-        } else {
-            mChosenImportance = IMPORTANCE_NONE;
-            confirmationText.setText(R.string.notification_channel_disabled);
+        switch (action) {
+            case SWAP_CONTENT_UNDO:
+                mChosenImportance = mStartingChannelImportance;
+                break;
+            case SWAP_CONTENT_TOGGLE_SILENT:
+                if (mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT) {
+                    mChosenImportance = IMPORTANCE_LOW;
+                    confirmationText.setText(R.string.notification_channel_silenced);
+                } else {
+                    mChosenImportance = IMPORTANCE_HIGH;
+                    confirmationText.setText(R.string.notification_channel_unsilenced);
+                }
+                break;
+            case SWAP_CONTENT_BLOCK:
+                if (mIsForeground) {
+                    mChosenImportance = IMPORTANCE_MIN;
+                    confirmationText.setText(R.string.notification_channel_minimized);
+                } else {
+                    mChosenImportance = IMPORTANCE_NONE;
+                    confirmationText.setText(R.string.notification_channel_disabled);
+                }
+                break;
+            default:
+                throw new IllegalArgumentException();
         }
 
+        boolean isUndo = action == SWAP_CONTENT_UNDO;
         ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
-                prompt.getAlpha(), showPrompt ? 1f : 0f);
-        promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
+                prompt.getAlpha(), isUndo ? 1f : 0f);
+        promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
         ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
-                confirmation.getAlpha(), showPrompt ? 0f : 1f);
-        confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
+                confirmation.getAlpha(), isUndo ? 0f : 1f);
+        confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
 
-        prompt.setVisibility(showPrompt ? VISIBLE : GONE);
-        confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
-        header.setVisibility(showPrompt ? VISIBLE : GONE);
+        prompt.setVisibility(isUndo ? VISIBLE : GONE);
+        confirmation.setVisibility(isUndo ? GONE : VISIBLE);
+        header.setVisibility(isUndo ? VISIBLE : GONE);
 
         mExpandAnimation = new AnimatorSet();
         mExpandAnimation.playTogether(promptAnim, confirmAnim);
@@ -413,8 +483,8 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (!cancelled) {
-                    prompt.setVisibility(showPrompt ? VISIBLE : GONE);
-                    confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
+                    prompt.setVisibility(isUndo ? VISIBLE : GONE);
+                    confirmation.setVisibility(isUndo ? GONE : VISIBLE);
                 }
             }
         });
@@ -428,6 +498,25 @@
     }
 
     @Override
+    public void onFinishedClosing() {
+        mStartingChannelImportance = mChosenImportance;
+        if (mChosenImportance != IMPORTANCE_UNSPECIFIED) {
+            mStartingChannelOrNotificationImportance = mChosenImportance;
+        }
+        mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
+
+        View prompt = findViewById(R.id.prompt);
+        ViewGroup confirmation = findViewById(R.id.confirmation);
+        View header = findViewById(R.id.header);
+        prompt.setVisibility(VISIBLE);
+        prompt.setAlpha(1f);
+        confirmation.setVisibility(GONE);
+        confirmation.setAlpha(1f);
+        header.setVisibility(VISIBLE);
+        header.setAlpha(1f);
+    }
+
+    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         if (mGutsContainer != null &&
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index 0251f64..8e6bfe3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -437,15 +437,15 @@
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
                         outRanking.canShowBadge(), outRanking.getUserSentiment(), true,
-                        false, null, null);
+                        false, false, null, null);
             } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
                 outRanking.populate(key, outRanking.getRank(),
                         outRanking.matchesInterruptionFilter(),
                         outRanking.getVisibilityOverride(), 255,
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
-                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true, false, null,
-                        null);
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true, false,
+                        false, null, null);
             } else {
                 outRanking.populate(key, outRanking.getRank(),
                         outRanking.matchesInterruptionFilter(),
@@ -453,8 +453,7 @@
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null,
                         outRanking.canShowBadge(), outRanking.getUserSentiment(), false, false,
-                        null,
-                        null);
+                        false, null, null);
             }
             return true;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 9c68e7d..9f8a5cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -167,7 +167,7 @@
                     0,
                     NotificationManager.IMPORTANCE_DEFAULT,
                     null, null,
-                    null, null, null, true, sentiment, false, false, null, null);
+                    null, null, null, true, sentiment, false, false, false, null, null);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
     }
@@ -186,7 +186,7 @@
                     null, null,
                     null, null, null, true,
                     NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, false,
-                    smartActions, null);
+                    false, smartActions, null);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index ee35449..626726d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -19,8 +19,10 @@
 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 android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEGATIVE;
 
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
@@ -34,15 +36,14 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -57,11 +58,12 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager
+        .OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
@@ -71,8 +73,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.junit.MockitoRule;
 import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 /**
  * Tests for {@link NotificationGutsManager}.
@@ -84,7 +86,7 @@
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
 
     private NotificationChannel mTestNotificationChannel = new NotificationChannel(
-            TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+            TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
     private TestableLooper mTestableLooper;
     private Handler mHandler;
     private NotificationTestHelper mHelper;
@@ -297,7 +299,9 @@
                 eq(false),
                 eq(false),
                 eq(true) /* isForBlockingHelper */,
-                eq(true) /* isUserSentimentNegative */);
+                eq(true) /* isUserSentimentNegative */,
+                eq(false) /*isNoisy */,
+                eq(0));
     }
 
     @Test
@@ -324,7 +328,69 @@
                 eq(false),
                 eq(false),
                 eq(false) /* isForBlockingHelper */,
-                eq(true) /* isUserSentimentNegative */);
+                eq(true) /* isUserSentimentNegative */,
+                eq(false) /*isNoisy */,
+                eq(0));
+    }
+
+    @Test
+    public void testInitializeNotificationInfoView_noisy() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(true);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        row.getEntry().noisy = true;
+        when(row.getIsNonblockable()).thenReturn(false);
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                any(NotificationChannel.class),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                eq(false),
+                eq(false),
+                eq(true) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */,
+                eq(true) /*isNoisy */,
+                eq(0));
+    }
+
+    @Test
+    public void testInitializeNotificationInfoView_importance() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(true);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        row.getEntry().importance = IMPORTANCE_DEFAULT;
+        when(row.getIsNonblockable()).thenReturn(false);
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                any(NotificationChannel.class),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                eq(false),
+                eq(false),
+                eq(true) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */,
+                eq(false) /*isNoisy */,
+                eq(IMPORTANCE_DEFAULT));
     }
 
     @Test
@@ -352,7 +418,9 @@
                 eq(true),
                 eq(false),
                 eq(false) /* isForBlockingHelper */,
-                eq(true) /* isUserSentimentNegative */);
+                eq(true) /* isUserSentimentNegative */,
+                eq(false) /*isNoisy */,
+                eq(0));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index ca968a8..3744196 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -17,11 +17,14 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
@@ -56,6 +59,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.IBinder;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -72,6 +76,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -150,6 +155,15 @@
                 IMPORTANCE_LOW);
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
                 new Notification(), UserHandle.CURRENT, null, 0);
+
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+    }
+
+    @After
+    public void tearDown() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
     }
 
     // TODO: if tests are taking too long replace this with something that makes the animation
@@ -172,7 +186,8 @@
     public void testBindNotification_SetsTextApplicationName() throws Exception {
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -184,7 +199,8 @@
         when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
                 .thenReturn(iconDrawable);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -192,7 +208,8 @@
     @Test
     public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(GONE, groupNameView.getVisibility());
         final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -208,7 +225,8 @@
                 eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
                 .thenReturn(notificationChannelGroup);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -219,7 +237,8 @@
     @Test
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -228,7 +247,7 @@
     public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
-                false);
+                false, false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, textView.getVisibility());
     }
@@ -241,7 +260,7 @@
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
-                false);
+                false, false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -249,7 +268,8 @@
     @Test
     public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -257,18 +277,71 @@
     @Test
     public void testBindNotification_BlockButton() throws Exception {
        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+               TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+               false, IMPORTANCE_DEFAULT);
         final View block = mNotificationInfo.findViewById(R.id.block);
+        final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
         assertEquals(VISIBLE, block.getVisibility());
+        assertEquals(GONE, toggleSilent.getVisibility());
         assertEquals(GONE, minimize.getVisibility());
     }
 
     @Test
+    public void testBindNotification_SilenceButton() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_DEFAULT);
+        final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
+        assertEquals(VISIBLE, toggleSilent.getVisibility());
+        assertEquals(
+                mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText());
+    }
+
+    @Test
+    public void testBindNotification_UnSilenceButton() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_LOW);
+        final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
+        assertEquals(VISIBLE, toggleSilent.getVisibility());
+        assertEquals(
+                mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText());
+    }
+
+    @Test
+    public void testBindNotification_SilenceButton_ChannelImportanceUnspecified() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_DEFAULT);
+        final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
+        assertEquals(VISIBLE, toggleSilent.getVisibility());
+        assertEquals(
+                mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText());
+    }
+
+    @Test
+    public void testBindNotification_UnSilenceButton_ChannelImportanceUnspecified()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_LOW);
+        final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
+        assertEquals(VISIBLE, toggleSilent.getVisibility());
+        assertEquals(
+                mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText());
+    }
+
+    @Test
     public void testBindNotification_MinButton() throws Exception {
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         final View block = mNotificationInfo.findViewById(R.id.block);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
         assertEquals(GONE, block.getVisibility());
@@ -283,7 +356,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
                     latch.countDown();
-                }, null, true, false);
+                }, null, true, false, false, IMPORTANCE_DEFAULT);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         settingsButton.performClick();
@@ -294,7 +367,8 @@
     @Test
     public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -306,7 +380,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
-                }, null, false, false);
+                }, null, false, false, false, IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -314,11 +388,12 @@
     @Test
     public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
-                }, null, true, false);
+                }, null, true, false, false, IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
     }
@@ -326,7 +401,8 @@
     @Test
     public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
     }
@@ -335,7 +411,7 @@
     public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
-                true, true);
+                true, true, false, IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
     }
@@ -348,7 +424,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(null, c);
                     latch.countDown();
-                }, null, true, true);
+                }, null, true, true, false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.info).performClick();
         // Verify that listener was triggered.
@@ -361,7 +437,7 @@
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true, true);
+                null, true, true, false, IMPORTANCE_DEFAULT);
         final TextView channelNameView =
                 mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, channelNameView.getVisibility());
@@ -372,7 +448,7 @@
     public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true, true);
+                null, true, true, false, IMPORTANCE_DEFAULT);
         final TextView blockView = mNotificationInfo.findViewById(R.id.block);
         assertEquals(GONE, blockView.getVisibility());
     }
@@ -381,7 +457,7 @@
     public void testbindNotification_BlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
-                true, true);
+                true, true, false, IMPORTANCE_DEFAULT);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -390,7 +466,8 @@
     @Test
     public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -400,7 +477,8 @@
     @Test
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -410,7 +488,8 @@
     public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -423,7 +502,8 @@
             throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         mTestableLooper.processAllMessages();
@@ -432,11 +512,40 @@
     }
 
     @Test
+    public void testDoesNotUpdateNotificationChannelAfterImportanceChangedSilenced()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_DEFAULT);
+
+        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+    }
+
+    @Test
+    public void testDoesNotUpdateNotificationChannelAfterImportanceChangedUnSilenced()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_DEFAULT);
+
+        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+    }
+
+    @Test
     public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
             throws Exception {
         int originalImportance = mNotificationChannel.getImportance();
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -450,7 +559,8 @@
             throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -468,7 +578,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                true, false /* isNonblockable */);
+                true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -489,7 +599,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                true, false /* isNonblockable */);
+                true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -510,7 +620,7 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
+                true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
 
         NotificationGuts guts = spy(new NotificationGuts(mContext, null));
         when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -538,7 +648,7 @@
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
+                true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
 
         NotificationGuts guts = spy(new NotificationGuts(mContext, null));
         when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -566,7 +676,8 @@
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true, true /* isUserSentimentNegative */);
+                true, true /* isUserSentimentNegative */, false, /* isNoisy */
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
 
@@ -585,7 +696,7 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
+                true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -607,7 +718,8 @@
                 false /* isNonblockable */,
                 true /* isForBlockingHelper */,
                 true,
-                false /* isUserSentimentNegative */);
+                false /* isUserSentimentNegative */,
+                false, /* isNoisy */IMPORTANCE_DEFAULT);
         NotificationGuts guts = mock(NotificationGuts.class);
         doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
         mNotificationInfo.setGutsParent(guts);
@@ -621,7 +733,8 @@
     public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
 
@@ -634,7 +747,8 @@
     public void testBlockChangedCallsUpdateNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -666,7 +780,8 @@
                 true /*provisioned */,
                 false /* isNonblockable */,
                 true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
+                true /* isUserSentimentNegative */,
+                false, /* isNoisy */IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -687,7 +802,8 @@
     public void testNonBlockableAppDoesNotBecomeMin() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
 
@@ -701,7 +817,8 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -721,7 +838,8 @@
     public void testKeepUpdatesNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -738,7 +856,8 @@
     public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -759,7 +878,8 @@
     public void testMinUndoDoesNotMinNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -777,10 +897,97 @@
     }
 
     @Test
+    public void testSilenceCallsUpdateNotificationChannel() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_DEFAULT);
+
+        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
+    }
+
+    @Test
+    public void testUnSilenceCallsUpdateNotificationChannel() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
+
+        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
+    }
+
+    @Test
+    public void testSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_DEFAULT);
+
+        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
+    }
+
+    @Test
+    public void testUnSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_LOW);
+
+        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
+    }
+
+    @Test
     public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -795,7 +1002,8 @@
     public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -812,7 +1020,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
-                }, null, null, true, true);
+                }, null, null, true, true, false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -830,7 +1038,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
                     saveImportance.run();
-                }, null, null, true, false);
+                }, null, null, true, false, false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -866,7 +1074,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
                 (View v, Intent intent) -> {
                     latch.countDown();
-                }, true, false);
+                }, true, false, false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(View.VISIBLE, settingsLink.getVisibility());
         settingsLink.performClick();
@@ -894,7 +1102,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
                 (View v, Intent intent) -> {
                     latch.countDown();
-                }, true, false);
+                }, true, false, false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(View.VISIBLE, settingsLink.getVisibility());
         settingsLink.performClick();
@@ -913,7 +1121,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
-                null, true, false);
+                null, true, false, false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -933,7 +1141,8 @@
                 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -956,7 +1165,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
-                true, true);
+                true, true, false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -972,7 +1181,8 @@
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -984,7 +1194,8 @@
     public void testUndoText_block() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -993,10 +1204,39 @@
     }
 
     @Test
+    public void testUndoText_silence() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                true, IMPORTANCE_DEFAULT);
+
+        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        waitForUndoButton();
+        TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
+        assertEquals(mContext.getString(R.string.notification_channel_silenced),
+                confirmationText.getText());
+    }
+
+    @Test
+    public void testUndoText_unsilence() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                true, IMPORTANCE_DEFAULT);
+
+        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        waitForUndoButton();
+        TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
+        assertEquals(mContext.getString(R.string.notification_channel_unsilenced),
+                confirmationText.getText());
+    }
+
+    @Test
     public void testNoHeaderOnConfirmation() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1007,7 +1247,8 @@
     public void testHeaderOnUndo() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f87a5f7..b404c41 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6564,6 +6564,7 @@
         Bundle smartActions = new Bundle();
         Bundle smartReplies = new Bundle();
         Bundle audiblyAlerted = new Bundle();
+        Bundle noisy = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -6594,6 +6595,7 @@
             smartActions.putParcelableArrayList(key, record.getSmartActions());
             smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
             audiblyAlerted.putBoolean(key, record.getAudiblyAlerted());
+            noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null);
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -6605,7 +6607,7 @@
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
                 channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
-                smartActions, smartReplies, audiblyAlerted);
+                smartActions, smartReplies, audiblyAlerted, noisy);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 0d7b584..bcba15d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -114,6 +114,7 @@
         Bundle smartActions = new Bundle();
         Bundle smartReplies = new Bundle();
         Bundle audiblyAlerted = new Bundle();
+        Bundle noisy = new Bundle();
 
         for (int i = 0; i < mKeys.length; i++) {
             String key = mKeys[i];
@@ -134,12 +135,13 @@
             smartActions.putParcelableArrayList(key, getSmartActions(key, i));
             smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i));
             audiblyAlerted.putBoolean(key, audiblyAlerted(i));
+            noisy.putBoolean(key, getNoisy(i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
                 channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden,
-                smartActions, smartReplies, audiblyAlerted);
+                smartActions, smartReplies, audiblyAlerted, noisy);
         return update;
     }
 
@@ -195,6 +197,10 @@
         return index < 2;
     }
 
+    private boolean getNoisy(int index) {
+        return index < 1;
+    }
+
     private ArrayList<String> getPeople(String key, int index) {
         ArrayList<String> people = new ArrayList<>();
         for (int i = 0; i < index; i++) {