Merge "Zen: Maintain selection throughout rocker session." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index abf7ea2..d35610b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27253,6 +27253,7 @@
     method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
     method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public int setAudioAttributes(android.media.AudioAttributes);
     method public deprecated int setEngineByPackageName(java.lang.String);
     method public int setLanguage(java.util.Locale);
     method public deprecated int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
diff --git a/core/java/android/speech/tts/AudioPlaybackQueueItem.java b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
index c13b47f..b4ac429 100644
--- a/core/java/android/speech/tts/AudioPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
@@ -54,7 +54,11 @@
         final UtteranceProgressDispatcher dispatcher = getDispatcher();
 
         dispatcher.dispatchOnStart();
-        mPlayer = MediaPlayer.create(mContext, mUri);
+
+        int sessionId = mAudioParams.mSessionId;
+        mPlayer = MediaPlayer.create(
+                mContext, mUri, null, mAudioParams.mAudioAttributes,
+                sessionId > 0 ? sessionId : AudioSystem.AUDIO_SESSION_ALLOCATE);
         if (mPlayer == null) {
             dispatcher.dispatchOnError(TextToSpeech.ERROR_OUTPUT);
             return;
@@ -76,11 +80,8 @@
                     mDone.open();
                 }
             });
-            mPlayer.setAudioStreamType(mAudioParams.mStreamType);
+
             setupVolume(mPlayer, mAudioParams.mVolume, mAudioParams.mPan);
-            if (mAudioParams.mSessionId != AudioSystem.AUDIO_SESSION_ALLOCATE) {
-                mPlayer.setAudioSessionId(mAudioParams.mSessionId);
-            }
             mPlayer.start();
             mDone.block();
             finish();
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index b405de0..dc4e9d3 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -2,6 +2,7 @@
 
 package android.speech.tts;
 
+import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioTrack;
 import android.speech.tts.TextToSpeechService.AudioOutputParams;
@@ -214,9 +215,14 @@
                 = AudioTrack.getMinBufferSize(mSampleRateInHz, channelConfig, mAudioFormat);
         int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
 
-        AudioTrack audioTrack = new AudioTrack(mAudioParams.mStreamType, mSampleRateInHz,
-                channelConfig, mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM,
+        AudioFormat audioFormat = (new AudioFormat.Builder())
+                .setChannelMask(channelConfig)
+                .setEncoding(mAudioFormat)
+                .setSampleRate(mSampleRateInHz).build();
+        AudioTrack audioTrack = new AudioTrack(mAudioParams.mAudioAttributes,
+                audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM,
                 mAudioParams.mSessionId);
+
         if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
             Log.w(TAG, "Unable to create audio track.");
             audioTrack.release();
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 46077ed..d8b9b5f 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -524,6 +525,17 @@
         public static final String KEY_PARAM_STREAM = "streamType";
 
         /**
+         * Parameter key to specify the audio attributes to be used when
+         * speaking text or playing back a file. The value should be set
+         * using {@link TextToSpeech#setAudioAttributes(AudioAttributes)}.
+         *
+         * @see TextToSpeech#speak(String, int, HashMap)
+         * @see TextToSpeech#playEarcon(String, int, HashMap)
+         * @hide
+         */
+        public static final String KEY_PARAM_AUDIO_ATTRIBUTES = "audioAttributes";
+
+        /**
          * Parameter key to identify an utterance in the
          * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
          * spoken, a file has been played back or a silence duration has elapsed.
@@ -1357,6 +1369,25 @@
     }
 
     /**
+     * Sets the audio attributes to be used when speaking text or playing
+     * back a file.
+     *
+     * @param audioAttributes Valid AudioAttributes instance.
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS}.
+     */
+    public int setAudioAttributes(AudioAttributes audioAttributes) {
+        if (audioAttributes != null) {
+            synchronized (mStartLock) {
+                mParams.putParcelable(Engine.KEY_PARAM_AUDIO_ATTRIBUTES,
+                    audioAttributes);
+            }
+            return SUCCESS;
+        }
+        return ERROR;
+    }
+
+    /**
      * @return the engine currently in use by this TextToSpeech instance.
      * @hide
      */
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index a0743f7..4fea109 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -17,7 +17,7 @@
 
 import android.app.Service;
 import android.content.Intent;
-import android.media.AudioManager;
+import android.media.AudioAttributes;
 import android.media.AudioSystem;
 import android.net.Uri;
 import android.os.Binder;
@@ -608,12 +608,6 @@
         public final int mSessionId;
 
         /**
-         * Audio stream type. Must be one of the STREAM_ contants defined in
-         * {@link android.media.AudioManager}.
-         */
-        public final int mStreamType;
-
-        /**
          * Volume, in the range [0.0f, 1.0f]. The default value is
          * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
          */
@@ -625,42 +619,62 @@
          */
         public final float mPan;
 
+
+        /**
+         * Audio attributes, set by {@link TextToSpeech#setAudioAttributes}
+         * or created from the value of {@link TextToSpeech.Engine#KEY_PARAM_STREAM}.
+         */
+        public final AudioAttributes mAudioAttributes;
+
         /** Create AudioOutputParams with default values */
         AudioOutputParams() {
             mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
-            mStreamType = Engine.DEFAULT_STREAM;
             mVolume = Engine.DEFAULT_VOLUME;
             mPan = Engine.DEFAULT_PAN;
+            mAudioAttributes = null;
         }
 
-        AudioOutputParams(int sessionId, int streamType, float volume, float pan) {
+        AudioOutputParams(int sessionId, float volume, float pan,
+                AudioAttributes audioAttributes) {
             mSessionId = sessionId;
-            mStreamType = streamType;
             mVolume = volume;
             mPan = pan;
+            mAudioAttributes = audioAttributes;
         }
 
         /** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */
-        static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle) {
+        static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle,
+                boolean isSpeech) {
             if (paramsBundle == null) {
                 return new AudioOutputParams();
             }
 
+            AudioAttributes audioAttributes =
+                    (AudioAttributes) paramsBundle.getParcelable(
+                            Engine.KEY_PARAM_AUDIO_ATTRIBUTES);
+            if (audioAttributes == null) {
+                int streamType = paramsBundle.getInt(
+                        Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
+                audioAttributes = (new AudioAttributes.Builder())
+                        .setLegacyStreamType(streamType)
+                        .setContentType((isSpeech ?
+                                AudioAttributes.CONTENT_TYPE_SPEECH :
+                                AudioAttributes.CONTENT_TYPE_SONIFICATION))
+                        .build();
+            }
+
             return new AudioOutputParams(
                     paramsBundle.getInt(
                             Engine.KEY_PARAM_SESSION_ID,
                             AudioSystem.AUDIO_SESSION_ALLOCATE),
-                    paramsBundle.getInt(
-                            Engine.KEY_PARAM_STREAM,
-                            Engine.DEFAULT_STREAM),
                     paramsBundle.getFloat(
                             Engine.KEY_PARAM_VOLUME,
                             Engine.DEFAULT_VOLUME),
                     paramsBundle.getFloat(
                             Engine.KEY_PARAM_PAN,
-                            Engine.DEFAULT_PAN));
+                            Engine.DEFAULT_PAN),
+                    audioAttributes);
         }
-
     }
 
 
@@ -832,7 +846,7 @@
         }
 
         AudioOutputParams getAudioParams() {
-            return AudioOutputParams.createFromV1ParamsBundle(mParams);
+            return AudioOutputParams.createFromV1ParamsBundle(mParams, true);
         }
     }
 
@@ -1005,6 +1019,11 @@
         public String getUtteranceId() {
             return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null);
         }
+
+        @Override
+        AudioOutputParams getAudioParams() {
+            return AudioOutputParams.createFromV1ParamsBundle(mParams, false);
+        }
     }
 
     private class SilenceSpeechItem extends UtteranceSpeechItem {
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 11f50ee..8b1c2b7 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -78,4 +78,11 @@
         android:orientation="vertical"
         android:paddingTop="3dp" />
 
+    <TextView
+        android:id="@+id/zen_alarm_warning"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="@dimen/qs_panel_padding"
+        android:gravity="center"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
 </com.android.systemui.volume.ZenModePanel>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a7f894c..a1064fd 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -38,6 +38,7 @@
     <color name="system_secondary_color">#ff384248</color>
     <color name="system_accent_color">#ff80CBC4</color><!-- deep teal 200 -->
     <color name="system_warning_color">#fff4511e</color><!-- deep orange 600 -->
+    <color name="qs_text">#FFFFFFFF</color>
     <color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
     <color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
     <color name="qs_subhead">#66FFFFFF</color><!-- 40% white -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 28b0853..c8c8e9a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -617,8 +617,8 @@
     <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
     <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
 
-    <!-- Zen mode: No interruptions title, with a warning about alarms and timers. [CHAR LIMIT=60] -->
-    <string name="zen_no_interruptions_with_warning">No interruptions, including alarms and timers</string>
+    <!-- Zen mode: No interruptions title, with a warning about alarms. [CHAR LIMIT=60] -->
+    <string name="zen_no_interruptions_with_warning">No interruptions, including alarms</string>
 
     <!-- Zen mode: No interruptions. [CHAR LIMIT=40] -->
     <string name="zen_no_interruptions">No interruptions</string>
@@ -626,6 +626,15 @@
     <!-- Zen mode: Only important interruptions. [CHAR LIMIT=40] -->
     <string name="zen_important_interruptions">Priority interruptions only</string>
 
+    <!-- Zen mode: Next alarm information - just a time. [CHAR LIMIT=40] -->
+    <string name="zen_alarm_information_time">Your next alarm is at <xliff:g id="alarm_time" example="5:00 PM">%s</xliff:g></string>
+
+    <!-- Zen mode: Next alarm information - day and time. [CHAR LIMIT=40] -->
+    <string name="zen_alarm_information_day_time">Your next alarm is <xliff:g id="alarm_day_and_time" example="Fri 5:00 PM">%s</xliff:g></string>
+
+    <!-- Zen mode: Next alarm warning. [CHAR LIMIT=40] -->
+    <string name="zen_alarm_warning">You won\'t hear your alarm at <xliff:g id="alarm_time" example="5:00 PM">%s</xliff:g></string>
+
     <!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
     <string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 708d3e8..5cc987a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -154,7 +154,7 @@
 
     <style name="TextAppearance.QS">
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">#ffffff</item>
+        <item name="android:textColor">@color/qs_text</item>
         <item name="android:fontFamily">sans-serif</item>
     </style>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 8564409..72c12d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -549,6 +549,8 @@
 
         mDozeServiceHost = new DozeServiceHost();
         putComponent(DozeService.Host.class, mDozeServiceHost);
+
+        setControllerUsers();
     }
 
     // ================================================================================
@@ -2797,6 +2799,13 @@
         animateCollapsePanels();
         updateNotifications();
         resetUserSetupObserver();
+        setControllerUsers();
+    }
+
+    private void setControllerUsers() {
+        if (mZenModeController != null) {
+            mZenModeController.setUserId(mCurrentUserId);
+        }
     }
 
     private void resetUserSetupObserver() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index dc06ec2..574d536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -285,9 +285,6 @@
         mDateExpanded.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
         mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
         mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
-        if (mStatusIcons != null) {
-            mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
-        }
         if (mSignalCluster != null) {
             mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
         }
@@ -440,12 +437,13 @@
     public void attachSystemIcons(LinearLayout systemIcons) {
         mSystemIconsContainer.addView(systemIcons);
         mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
+        mStatusIcons.addOnLayoutChangeListener(mStatusIconsChanged);
         mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
     }
 
     public void onSystemIconsDetached() {
         if (mStatusIcons != null) {
-            mStatusIcons.setVisibility(View.VISIBLE);
+            mStatusIcons.removeOnLayoutChangeListener(mStatusIconsChanged);
         }
         if (mSignalCluster != null) {
             mSignalCluster.setVisibility(View.VISIBLE);
@@ -530,6 +528,23 @@
         // here.
     }
 
+    private final OnLayoutChangeListener mStatusIconsChanged = new OnLayoutChangeListener() {
+        private final Rect mClipBounds = new Rect();
+
+        @Override
+        public void onLayoutChange(View v, int left, int top, int right,
+                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+            // Hide the statusIcons in the header by clipping them.  Can't touch visibility since
+            // they are mirrored to the real status bar.
+            final Rect r = mSystemIconsContainer.getClipBounds();
+            if (r == null || r.left != right) {
+                mClipBounds.set(right, 0, mSystemIconsContainer.getWidth(),
+                        mSystemIconsContainer.getHeight());
+                mSystemIconsContainer.setClipBounds(mClipBounds);
+            }
+        }
+    };
+
     private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
         @Override
         public void onToggleStateChanged(final boolean state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 61902a2..2e97d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -27,11 +27,13 @@
     void requestConditions(boolean request);
     void setExitConditionId(Uri exitConditionId);
     Uri getExitConditionId();
-    boolean hasNextAlarm();
+    long getNextAlarm();
+    void setUserId(int userId);
 
     public static class Callback {
         public void onZenChanged(int zen) {}
         public void onExitConditionChanged(Uri exitConditionId) {}
         public void onConditionsChanged(Condition[] conditions) {}
+        public void onNextAlarmChanged() {}
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 7703966..ae037f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -16,19 +16,25 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.app.AlarmClockInfo;
+import android.app.AlarmManager;
 import android.app.INotificationManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.IConditionListener;
 import android.service.notification.ZenModeConfig;
+import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.qs.GlobalSetting;
 
 import java.util.ArrayList;
@@ -36,8 +42,8 @@
 
 /** Platform implementation of the zen mode controller. **/
 public class ZenModeControllerImpl implements ZenModeController {
-    private static final String TAG = "ZenModeControllerImpl";
-    private static final boolean DEBUG = false;
+    private static final String TAG = "ZenModeController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final Context mContext;
@@ -45,13 +51,13 @@
     private final GlobalSetting mConfigSetting;
     private final INotificationManager mNoMan;
     private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
-    private final LockPatternUtils mUtils;
+    private final AlarmManager mAlarmManager;
 
+    private int mUserId;
     private boolean mRequesting;
 
     public ZenModeControllerImpl(Context context, Handler handler) {
         mContext = context;
-        mUtils = new LockPatternUtils(mContext);
         mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
             @Override
             protected void handleValueChanged(int value) {
@@ -68,6 +74,7 @@
         mConfigSetting.setListening(true);
         mNoMan = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
     }
 
     @Override
@@ -126,8 +133,22 @@
     }
 
     @Override
-    public boolean hasNextAlarm() {
-        return mUtils.getNextAlarm() != null;
+    public long getNextAlarm() {
+        final AlarmClockInfo info = mAlarmManager.getNextAlarmClock(mUserId);
+        return info != null ? info.getTriggerTime() : 0;
+    }
+
+    @Override
+    public void setUserId(int userId) {
+        mUserId = userId;
+        mContext.registerReceiverAsUser(mReceiver, new UserHandle(mUserId),
+                new IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED), null, null);
+    }
+
+    private void fireNextAlarmChanged() {
+        for (Callback cb : mCallbacks) {
+            cb.onNextAlarmChanged();
+        }
     }
 
     private void fireZenChanged(int zen) {
@@ -169,4 +190,13 @@
             updateConditions(conditions);
         }
     };
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(intent.getAction())) {
+                fireNextAlarmChanged();
+            }
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 2e93c5b..33cf3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -30,6 +30,8 @@
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
+import android.text.format.DateFormat;
+import android.text.format.Time;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -46,7 +48,10 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
+import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.Date;
+import java.util.Locale;
 import java.util.Objects;
 
 public class ZenModePanel extends LinearLayout {
@@ -75,6 +80,8 @@
     private final H mHandler = new H();
     private final Favorites mFavorites;
     private final Interpolator mFastOutSlowInInterpolator;
+    private final int mTextColor;
+    private final int mAccentColor;
 
     private char mLogTag = '?';
     private String mTag;
@@ -85,6 +92,7 @@
     private TextView mZenSubheadExpanded;
     private View mMoreSettings;
     private LinearLayout mZenConditions;
+    private TextView mAlarmWarning;
 
     private Callback mCallback;
     private ZenModeController mController;
@@ -95,6 +103,7 @@
     private int mSessionZen;
     private Uri mSessionExitConditionId;
     private String mExitConditionText;
+    private long mNextAlarm;
 
     public ZenModePanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -103,6 +112,8 @@
         mInflater = LayoutInflater.from(mContext.getApplicationContext());
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
                 android.R.interpolator.fast_out_slow_in);
+        mTextColor = mContext.getResources().getColor(R.color.qs_text);
+        mAccentColor = mContext.getResources().getColor(R.color.system_accent_color);
         updateTag();
         if (DEBUG) Log.d(mTag, "new ZenModePanel");
     }
@@ -145,6 +156,7 @@
         });
 
         mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
+        mAlarmWarning = (TextView) findViewById(R.id.zen_alarm_warning);
     }
 
     @Override
@@ -154,6 +166,7 @@
         mSessionZen = getSelectedZen(-1);
         mSessionExitConditionId = mExitConditionId;
         refreshExitConditionText();
+        refreshNextAlarm();
         updateWidgets();
     }
 
@@ -216,6 +229,7 @@
         if (Objects.equals(mExitConditionId, exitConditionId)) return;
         mExitConditionId = exitConditionId;
         refreshExitConditionText();
+        updateWidgets();
     }
 
     private void refreshExitConditionText() {
@@ -270,12 +284,34 @@
         final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
         final boolean foreverSelected = mExitConditionId == null;
+        final boolean hasNextAlarm = mNextAlarm != 0;
 
         mZenSubhead.setVisibility(!zenOff && (mExpanded || !foreverSelected) ? VISIBLE : GONE);
         mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE);
         mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
         mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
         mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
+        mAlarmWarning.setVisibility(zenNone && mExpanded && hasNextAlarm ? VISIBLE : GONE);
+
+        if (zenNone && mExpanded && hasNextAlarm) {
+            final long exitTime = ZenModeConfig.tryParseCountdownConditionId(mExitConditionId);
+            final long now = System.currentTimeMillis();
+            final boolean alarmToday = time(mNextAlarm).yearDay == time(now).yearDay;
+            final String skeleton = (alarmToday ? "" : "E")
+                    + (DateFormat.is24HourFormat(mContext) ? "Hm" : "hma");
+            final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+            final String alarm = new SimpleDateFormat(pattern).format(new Date(mNextAlarm));
+            final boolean isWarning = exitTime > 0 && mNextAlarm > now && mNextAlarm < exitTime;
+            if (isWarning) {
+                mAlarmWarning.setText(mContext.getString(R.string.zen_alarm_warning, alarm));
+                mAlarmWarning.setTextColor(mAccentColor);
+            } else {
+                mAlarmWarning.setText(mContext.getString(alarmToday
+                        ? R.string.zen_alarm_information_time
+                        : R.string.zen_alarm_information_day_time, alarm));
+                mAlarmWarning.setTextColor(mTextColor);
+            }
+        }
 
         if (zenNone) {
             mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning);
@@ -286,6 +322,12 @@
         }
     }
 
+    private static Time time(long millis) {
+        final Time t = new Time();
+        t.set(millis);
+        return t;
+    }
+
     private Condition parseExistingTimeCondition(Uri conditionId) {
         final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
         if (time == 0) return null;
@@ -311,6 +353,15 @@
                 Condition.FLAG_RELEVANT_NOW);
     }
 
+    private void refreshNextAlarm() {
+        mNextAlarm = mController.getNextAlarm();
+    }
+
+    private void handleNextAlarmChanged() {
+        refreshNextAlarm();
+        updateWidgets();
+    }
+
     private void handleUpdateConditions(Condition[] conditions) {
         final int newCount = conditions == null ? 0 : conditions.length;
         if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
@@ -524,12 +575,18 @@
         public void onExitConditionChanged(Uri exitConditionId) {
             mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitConditionId).sendToTarget();
         }
+
+        @Override
+        public void onNextAlarmChanged() {
+            mHandler.sendEmptyMessage(H.NEXT_ALARM_CHANGED);
+        }
     };
 
     private final class H extends Handler {
         private static final int UPDATE_CONDITIONS = 1;
         private static final int EXIT_CONDITION_CHANGED = 2;
         private static final int UPDATE_ZEN = 3;
+        private static final int NEXT_ALARM_CHANGED = 4;
 
         private H() {
             super(Looper.getMainLooper());
@@ -538,12 +595,14 @@
         @Override
         public void handleMessage(Message msg) {
             if (msg.what == UPDATE_CONDITIONS) {
-                handleUpdateConditions((Condition[])msg.obj);
+                handleUpdateConditions((Condition[]) msg.obj);
                 checkForDefault();
             } else if (msg.what == EXIT_CONDITION_CHANGED) {
-                handleExitConditionChanged((Uri)msg.obj);
+                handleExitConditionChanged((Uri) msg.obj);
             } else if (msg.what == UPDATE_ZEN) {
                 handleUpdateZen(msg.arg1);
+            } else if (msg.what == NEXT_ALARM_CHANGED) {
+                handleNextAlarmChanged();
             }
         }
     }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index edeb5b4..abbaacc 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2271,6 +2271,22 @@
         }
 
         @Override
+        public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
+            if (mOutsetBottom != null) {
+                final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+                int bottom = (int) mOutsetBottom.getDimension(metrics);
+                WindowInsets newInsets = insets.replaceSystemWindowInsets(
+                        insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+                        insets.getSystemWindowInsetRight(),
+                        insets.getSystemWindowInsetBottom() + bottom);
+                return super.dispatchApplyWindowInsets(newInsets);
+            } else {
+                return super.dispatchApplyWindowInsets(insets);
+            }
+        }
+
+
+        @Override
         public boolean onTouchEvent(MotionEvent event) {
             return onInterceptTouchEvent(event);
         }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 65cb6c9..09cf392 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -269,13 +269,39 @@
 
             Intent launchIntent = new Intent(Intent.ACTION_MAIN);
             launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-            launchIntent.setComponent(component);
             launchIntent.setSourceBounds(sourceBounds);
             launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            launchIntent.setPackage(component.getPackageName());
 
             long ident = Binder.clearCallingIdentity();
             try {
-                mContext.startActivityAsUser(launchIntent, opts, user);
+                IPackageManager pm = AppGlobals.getPackageManager();
+                ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+                if (!info.exported) {
+                    throw new SecurityException("Cannot launch non-exported components "
+                            + component);
+                }
+
+                // Check that the component actually has Intent.CATEGORY_LAUCNCHER
+                // as calling startActivityAsUser ignores the category and just
+                // resolves based on the component if present.
+                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
+                        PackageManager.NO_CROSS_PROFILE, // We only want the apps for this user
+                        user.getIdentifier());
+                final int size = apps.size();
+                for (int i = 0; i < size; ++i) {
+                    ActivityInfo activityInfo = apps.get(i).activityInfo;
+                    if (activityInfo.packageName.equals(component.getPackageName()) &&
+                            activityInfo.name.equals(component.getClassName())) {
+                        // Found an activity with category launcher that matches
+                        // this component so ok to launch.
+                        launchIntent.setComponent(component);
+                        mContext.startActivityAsUser(launchIntent, opts, user);
+                        return;
+                    }
+                }
+                throw new SecurityException("Attempt to launch activity without "
+                        + " category Intent.CATEGORY_LAUNCHER " + component);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a3fd1df..e16894e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3171,25 +3171,34 @@
         file.delete();
         file = getUserPackagesStateBackupFile(userId);
         file.delete();
-        removeCrossProfileIntentFiltersToUserLPr(userId);
+        removeCrossProfileIntentFiltersLPw(userId);
         removeCrossProfilePackagesLPw(userId);
     }
 
-    void removeCrossProfileIntentFiltersToUserLPr(int targetUserId) {
-        for (int i = 0; i < mCrossProfileIntentResolvers.size(); i++) {
-            int sourceUserId = mCrossProfileIntentResolvers.keyAt(i);
-            CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId);
-            boolean needsWriting = false;
-            HashSet<CrossProfileIntentFilter> cpifs =
-                    new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
-            for (CrossProfileIntentFilter cpif : cpifs) {
-                if (cpif.getTargetUserId() == targetUserId) {
-                    needsWriting = true;
-                    cpir.removeFilter(cpif);
-                }
+    void removeCrossProfileIntentFiltersLPw(int userId) {
+        synchronized (mCrossProfileIntentResolvers) {
+            // userId is the source user
+            if (mCrossProfileIntentResolvers.get(userId) != null) {
+                mCrossProfileIntentResolvers.remove(userId);
+                writePackageRestrictionsLPr(userId);
             }
-            if (needsWriting) {
-                writePackageRestrictionsLPr(sourceUserId);
+            // userId is the target user
+            int count = mCrossProfileIntentResolvers.size();
+            for (int i = 0; i < count; i++) {
+                int sourceUserId = mCrossProfileIntentResolvers.keyAt(i);
+                CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId);
+                boolean needsWriting = false;
+                HashSet<CrossProfileIntentFilter> cpifs =
+                        new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
+                for (CrossProfileIntentFilter cpif : cpifs) {
+                    if (cpif.getTargetUserId() == userId) {
+                        needsWriting = true;
+                        cpir.removeFilter(cpif);
+                    }
+                }
+                if (needsWriting) {
+                    writePackageRestrictionsLPr(sourceUserId);
+                }
             }
         }
     }